1. GCD相关概念
任务:就是执行操作的意思,就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:『同步执行』 和 『异步执行』。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
同步执行(sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行(async):异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。可以在新的线程中执行任务,具备开启新线程的能力。
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。
串行队列(Serial Dispatch Queue):每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)。并发队列 的并发功能只有在异步(dispatch_async)方法下才有效。
2. GCD 的使用步骤
1.创建一个队列(串行队列或并发队列);
2.将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)。
3. 队列的创建方法 / 获取方法
使用 dispatch_queue_create 方法来创建队列。该方法需要传入两个参数,第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
对于串行队列,GCD 提供了的一种特殊的串行队列:『主队列(Main Dispatch Queue)』。所有放在主队列中的任务,都会放到主线程中执行。可使用 dispatch_get_main_queue() 方法获得主队列。
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();
对于并发队列,GCD 默认提供了 『全局并发队列(Global Dispatch Queue)』。
可以使用 dispatch_get_global_queue 方法来获取全局并发队列。需要传入两个参数。第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用 0 即可。
// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4. 任务的创建方法
// 同步执行任务创建方法
dispatch_sync(queue, ^{
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
});
5.1 队列和任务的组合使用
区别 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 | 死锁,卡住不执行 |
异步(async) | 有开启新线程,并发执行任务 | 有开启新线程(1条),串行执行任务 | 没有开启新线程,串行执行任务 |
从上边可看出:『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。
这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。
而如果我们在 『其他线程』 调用 『主队列』+『同步执行』,则不会阻塞 『主队列』,自然也不会造成死锁问题。最终的结果是:不会开启新线程,串行执行任务。
5.2 队列嵌套情况下,不同组合方式区别
区别 | 『异步执行+并发队列』嵌套『同一个并发队列』 | 『同步执行+并发队列』嵌套『同一个并发队列』 | 『异步执行+串行队列』嵌套『同一个串行队列』 | 『同步执行+串行队列』嵌套『同一个串行队列』 |
---|---|---|---|---|
同步(sync) | 没有开启新的线程,串行执行任务 | 没有开启新线程,串行执行任务 | 死锁卡住不执行 | 死锁卡住不执行 |
异步(async) | 有开启新线程,并发执行任务 | 有开启新线程,并发执行任务 | 有开启新线程(1 条),串行执行任务 | 有开启新线程(1 条),串行执行任务 |
6. 具体代码组合使用详情 ( 线程number=1 就是主线程 大于1就是子线程 )
- 同步(sync) + 并发队列 ( 没有开启子线程,串行执行任务,在主线程 )
// 同步(sync) + 并发队列 (没有开启子线程,串行执行任务,在主线程)
-(void)startGCDAction{
// 1.1 创建并发队列
// dispatch_queue_t queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
// 1.2 获取全局并发队列
dispatch_queue_t queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.0 同步
dispatch_sync(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(queueObj, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(queueObj, ^{
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
TestModel[85321:13209706] 做任务(1),当前线程:<NSThread: 0x281beda40>{number = 1, name = main}
TestModel[85321:13209706] 做任务(2),当前线程:<NSThread: 0x281beda40>{number = 1, name = main}
TestModel[85321:13209706] 做任务(3),当前线程:<NSThread: 0x281beda40>{number = 1, name = main}
- 异步(async) + 并发队列 ( 有开启子线程,并发执行任务,在子线程 )
// 异步(async) + 并发队列(有开启子线程,并发执行任务,在子线程)
-(void)startGCDAction{
// 1.1 创建并发队列
dispatch_queue_t queueObj = dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
// 1.2 获取全局并发队列
// dispatch_queue_t queueObj = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.0 异步
dispatch_async(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_async(queueObj, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
dispatch_async(queueObj, ^{
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
TestModel[85332:13211973] 做任务(2),当前线程:<NSThread: 0x281405a80>{number = 4, name = (null)}
TestModel[85332:13211979] 做任务(1),当前线程:<NSThread: 0x281408cc0>{number = 3, name = (null)}
TestModel[85332:13211973] 做任务(3),当前线程:<NSThread: 0x281405a80>{number = 4, name = (null)}
- 同步(sync) + 串行队列 ( 没有开启子线程,串行执行任务,在主线程 )
// 同步(sync) + 串行队列 (没有开启子线程,串行执行任务,在主线程)
-(void)startGCDAction{
// 1.0 创建串行队列
dispatch_queue_t queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
// 2.0 同步
dispatch_sync(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(queueObj, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(queueObj, ^{
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
TestModel[85392:13219612] 做任务(1),当前线程:<NSThread: 0x2822a5a40>{number = 1, name = main}
TestModel[85392:13219612] 做任务(2),当前线程:<NSThread: 0x2822a5a40>{number = 1, name = main}
TestModel[85392:13219612] 做任务(3),当前线程:<NSThread: 0x2822a5a40>{number = 1, name = main}
- 异步(async) + 串行队列 ( 只开启一条子线程,串行执行任务 )
// 异步(async) + 串行队列 (只开启一条子线程,串行执行任务)
-(void)startGCDAction{
// 1.0 创建串行队列
dispatch_queue_t queueObj = dispatch_queue_create("nameValue", DISPATCH_QUEUE_SERIAL);
// 2.0 异步
dispatch_async(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_async(queueObj, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
dispatch_async(queueObj, ^{
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
TestModel[85396:13221073] 做任务(1),当前线程:<NSThread: 0x28106c940>{number = 3, name = (null)}
TestModel[85396:13221073] 做任务(2),当前线程:<NSThread: 0x28106c940>{number = 3, name = (null)}
TestModel[85396:13221073] 做任务(3),当前线程:<NSThread: 0x28106c940>{number = 3, name = (null)}
- 同步(sync) + 主线程 ( 造成死锁,主线程和同步任务相互等待 )
// 同步(sync) + 主线程 (造成死锁,主线程和同步任务相互等待)
-(void)startGCDActionWithMainThread{
// 1.0 获取主线程
dispatch_queue_main_t mainQueue = dispatch_get_main_queue();
// 2.0 同步
dispatch_sync(mainQueue, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_sync(mainQueue, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
}
最终运行结果:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1004cb84c)
在 dispatch_sync(mainQueue, ^{ }); 报上诉错误 三个都不会打印 因为程序直接奔溃
- 异步(async) + 主线程 ( 没有开启子线程,串行执行任务,在主线程 )
// 异步(async) + 主线程 (没有开启子线程,串行执行任务,在主线程)
-(void)startGCDActionWithMainThread{
// 1.0 获取主线程
dispatch_queue_main_t mainQueue = dispatch_get_main_queue();
// 2.0 异步
dispatch_async(mainQueue, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
TestModel[85332:13211973] 做任务(1),当前线程:<NSThread: 0x280406e40>{number = 1, name = main}
TestModel[85332:13211973] 做任务(2),当前线程:<NSThread: 0x280406e40>{number = 1, name = main}
7. GCD 线程之间的通信
在其子线程中完成了耗时操作后,需要回到主线程 (进行 UI 刷新) ,那么就用到了线程之间的通讯。通过使用
1.1 获取主线程 dispatch_queue_t mainQueue = dispatch_get_main_queue();
1.2 异步回到主线程 dispatch_async(mainQueue, ^{ } );
// 线程之间的通讯(子线程回到主线程)
-(void)startGCDCommunicationAction{
// 1.1 创建并发队列
// dispatch_queue_t queueObj=dispatch_queue_create("nameValue",DISPATCH_QUEUE_CONCURRENT);
// 1.2 获取全局并发队列
dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.0 异步
dispatch_async(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.0]; // 睡眠线程3秒,相当于耗时操作
// 3.0 获取主线程
dispatch_queue_t mainQueue=dispatch_get_main_queue();
// 4.0 异步回到主线程
dispatch_async(mainQueue, ^{
NSLog(@"回到主线程刷新UI操作,当前线程:%@",[NSThread currentThread]);
});
});
}
最终运行结果:
2019-08-11 12:26:51.973353 TestModel[85877:13291589] 做任务(1),当前线程:<NSThread: 0x282f797c0>{number = 4, name = (null)}
2019-08-11 12:26:54.979122 TestModel[85877:13291523] 回到主线程刷新UI操作,当前线程:<NSThread: 0x282f1ae40>{number = 1, name = main}
8. GCD 的其他方法
1. 栅栏函数(方法):dispatch_barrier_sync (同步 ) 和 dispatch_barrier_async (异步 ) 执行完栅栏前面的操作之后,才执行栅栏操作(子线程或主线程),最后再执行栅栏后边的操作。
// 使用栅栏函数,必须通过创建并发队列 不能使用全局并发队列
-(void)startGCDWithBarrier{
// 1.0 创建并发队列 (这里不能用下面的 直接获取全局并发队列 只能通过创建才能体现出栅栏函数)
dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
// 1.0 获取全局并发队列
// dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.0 异步操作1
dispatch_async(queueObj, ^{
NSLog(@"做任务(1),当前线程:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0]; // 睡眠线程1秒
});
// 2.0 异步操作2
dispatch_async(queueObj, ^{
NSLog(@"做任务(2),当前线程:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0]; // 睡眠线程2秒
});
// 3.0 同步栅栏函数(在主线程执行操作,换成 async 就在子线程中)
dispatch_barrier_sync(queueObj, ^{
NSLog(@"栅栏函数做任务(😁😁),当前线程:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0]; // 睡眠线程2秒
});
// 2.0异步操作3
dispatch_async(queueObj, ^{
NSLog(@"做任务(3),当前线程:%@",[NSThread currentThread]);
});
// 2.0异步操作4
dispatch_async(queueObj, ^{
NSLog(@"做任务(4),当前线程:%@",[NSThread currentThread]);
});
}
最终运行结果:
2019-08-11 12:49:37.631269 TestModel[85906:13296017] 做任务(1),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:38.632798 TestModel[85906:13296017] 做任务(2),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:40.634186 TestModel[85906:13295953] 栅栏函数做任务(😁😁),当前线程:<NSThread: 0x2806d3040>{number = 1, name = main}
2019-08-11 12:49:41.635503 TestModel[85906:13296017] 做任务(3),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2019-08-11 12:49:41.635610 TestModel[85906:13296017] 做任务(4),当前线程:<NSThread: 0x2806b1a00>{number = 4, name = (null)}
2. 延时执行函数(方法):dispatch_after 其并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中(一定在主线程)。
// GCD延时函数
-(void)startGCDWithAfterFun{
NSLog(@"来咯!来咯!她们都来了 😝 ");
// 获取全局的并发队列
dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queueObj, ^{
NSLog(@"调用前的线程是:%@ ",[NSThread currentThread]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3秒后才调用我这个操作,当前线程是:%@ ",[NSThread currentThread]);
});
});
}
最终运行结果:
2019-08-11 13:09:48.371427 TestModel[85920:13298751] 来咯!来咯!她们都来了 😝
2019-08-11 13:09:48.371942 TestModel[85920:13298827] 调用前的线程是:<NSThread: 0x280b38f80>{number = 4, name = (null)}
2019-08-11 13:09:51.613677 TestModel[85920:13298751] 3秒后才调用我这个操作,当前线程是:<NSThread: 0x280b55880>{number = 1, name = main}
3. 一次性函数(方法):dispatch_once 该函数中的代码整个程序运行过程中只会执行一次。
// GCD一次性函数(只会执行一次)
-(void)startGCDWithOnceFun{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"你只能打我一次哦(😁😁😁),不服来战!");
});
}
我在 -(void)viewDidLoad {} 中调用多次,最终结果只会输出一次
-(void)viewDidLoad {
[super viewDidLoad];
[self startGCDWithOnceFun];
[self startGCDWithOnceFun];
[self startGCDWithOnceFun];
[self startGCDWithOnceFun];
}
最终运行结果:
TestModel[85923:13300176] 你只能打我一次哦(😁😁😁),不服来战!
4. GCD 队列组 dispatch_group_async 分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。监听队列组中任务完成 dispatch_group_notify
// GCD之队列组
-(void)startGCDWithGroup{
// 1.0 创建一个队列组
dispatch_group_t groupObj=dispatch_group_create();
// 2.1 创建并发队列
dispatch_queue_t queueObj=dispatch_queue_create("nameValue", DISPATCH_QUEUE_CONCURRENT);
// 2.2 获取全局的并发队列
// dispatch_queue_t queueObj=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 3.1 使用队列组执行任务(1)
dispatch_group_async(groupObj, queueObj, ^{
NSLog(@"列组中,子线程执行任务(1),当前线程是:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0];
});
// 3.1 使用队列组执行任务(1)
dispatch_group_async(groupObj, queueObj, ^{
NSLog(@"列组中,子线程队执行任务(2),当前线程是:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.0];
});
// 4.0 队列组中任务执行完毕 回到主线程
dispatch_group_notify(groupObj, dispatch_get_main_queue(), ^{
NSLog(@"队列组中任务执行完毕调用,回到主线程,当前线程是:%@",[NSThread currentThread]);
});
}
最终运行结果:
2019-08-11 17:07:44.674 TestModel[86321:13347506] 列组中,子线程队执行任务(2),当前线程是:<NSThread: 0x281365880>{number = 3, name = (null)}
2019-08-11 17:07:44.675 TestModel[86321:13347505] 列组中,子线程执行任务(1),当前线程是:<NSThread: 0x281330940>{number = 4, name = (null)}
2019-08-11 17:07:47.680 TestModel[86321:13347495] 队列组中任务执行完毕调用,回到主线程,当前线程是:<NSThread: 0x281351340>{number = 1, name = main}
5. dispatch_group_wait : 暂停当前线程(阻塞当前线程) dispatch_semaphore :(线程加锁)
网友评论