Grand Central Dispatch
纯C语言,提供了非常多强大的函数
线程相关知识以及GCD概念介绍不是本篇重点这里不做过多涉及
(一)OC篇
只想看swift的可以直接跳过,看下面的swift篇
队列的创建
- dispatch_queue_create(param1,param2)
/**
创建一个队列
@param "identifier" 队列的唯一标识
@param DISPATCH_QUEUE_SERIAL 串行队列还是并行队列
@return 返回实例对象(姑且认为它是个对象)
*/
dispatch_queue_t queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_SERIAL);
- param1需要传入一个c语言类型字符串,作用是用来标记当前queue,可以传空.可以通过dispatch_queue_get_label来获取queue的标记.
const *char s = dispatch_queue_get_label(queue);
-
param2传入Dispatch Queue Types,来决定queue的类型.
串行队列
DISPATCH_QUEUE_SERIAL
并行队列
DISPATCH_QUEUE_CONCURRENT
主队列 dispatch_get_main_queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局队列 dispatch_get_global_queue(param1, param2)
会获取一个全局队列,我们姑且理解为系统为我们开启的一些全局线程。我们用priority指定队列的优先级,而flag作为保留字段备用(一般为0)。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
- param1 传入队列优先级.
- param2 保留字段,目前没什么用,直接传0.
队列中任务的执行:
- 串行(Serial):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 并发(Concurrent):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效。
- 同步(Synchronous):在当前线程中执行任务,不具备开启新线程的能力
- 异步(Asynchronous):在新的线程中执行任务,具备开启新线程的能力
- 串行队列,同步执行
- (void)sync_serial {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("com.test.id", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
控制台日志如下:
2018-01-23 15:53:08.274847+0800 Thread[74746:3368362] mian_start
2018-01-23 15:53:08.275235+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:11.276825+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:11.277165+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:14.277633+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:14.277845+0800 Thread[74746:3368362] tread_begin ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:17.278809+0800 Thread[74746:3368362] tread_end ,<NSThread: 0x604000064e40>{number = 1, name = main}
2018-01-23 15:53:17.279073+0800 Thread[74746:3368362] mian_end
总结:不重新开辟线程,线程中的任务按顺序执行.
- 并行队列,同步执行
- (void)sync_Concurrent {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("222", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
dispatch_sync(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
打印日志如下:
2018-01-29 11:53:22.464506+0800 Thread[60281:937006] mian_start
2018-01-29 11:53:22.464832+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:25.465294+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:25.465514+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:28.466237+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:28.466548+0800 Thread[60281:937006] tread_begin ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:31.467731+0800 Thread[60281:937006] tread_end ,<NSThread: 0x600000073bc0>{number = 1, name = main}
2018-01-29 11:53:31.467904+0800 Thread[60281:937006] mian_end
总结:不开辟新线程,任务按顺序执行.
- 串行队列,异步执行
- (void)async_serial {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("333", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
日志如下:
2018-01-29 11:56:52.776368+0800 Thread[60281:937006] mian_start
2018-01-29 11:56:52.776701+0800 Thread[60281:937006] mian_end
2018-01-29 11:56:52.776777+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:55.779400+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:55.779647+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:58.784670+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:56:58.784963+0800 Thread[60281:951989] tread_begin ,<NSThread: 0x600000274780>{number = 5, name = (null)}
2018-01-29 11:57:01.786699+0800 Thread[60281:951989] tread_end ,<NSThread: 0x600000274780>{number = 5, name = (null)}
总结:只会另外开辟一个线程,线程中的任务按顺序执行.而且新开辟的线程不会阻塞当前线程.两个线程中的代码同时执行.
- 并行队列,异步执行
- (void)async_concurrent {
NSLog(@"mian_start");
dispatch_queue_t queue = dispatch_queue_create("333", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
dispatch_async(queue, ^{
[self taskBegin];
});
NSLog(@"mian_end");
}
- (void)taskBegin {
NSThread *thread = [NSThread currentThread];
NSLog(@"tread_begin ,%@",thread);
[NSThread sleepForTimeInterval:3];
NSLog(@"tread_end ,%@",thread);
}
日志如下:
2018-01-29 13:07:10.507523+0800 Thread[60281:937006] mian_start
2018-01-29 13:07:10.507810+0800 Thread[60281:937006] mian_end
2018-01-29 13:07:10.507901+0800 Thread[60281:1109308] tread_begin ,<NSThread: 0x6040004654c0>{number = 7, name = (null)}
2018-01-29 13:07:10.507903+0800 Thread[60281:951997] tread_begin ,<NSThread: 0x600000476d80>{number = 8, name = (null)}
2018-01-29 13:07:10.507926+0800 Thread[60281:1109326] tread_begin ,<NSThread: 0x60400046c740>{number = 9, name = (null)}
2018-01-29 13:07:13.512128+0800 Thread[60281:1109308] tread_end ,<NSThread: 0x6040004654c0>{number = 7, name = (null)}
2018-01-29 13:07:13.512173+0800 Thread[60281:951997] tread_end ,<NSThread: 0x600000476d80>{number = 8, name = (null)}
2018-01-29 13:07:13.512221+0800 Thread[60281:1109326] tread_end ,<NSThread: 0x60400046c740>{number = 9, name = (null)}
总结:会开启多个线程,.新开辟的线程不会相互阻塞.多个线程中的任务同时执行.
调度组 dispatch_group_t
项目中,我们有时候可能会遇到这样的场景.需要开启多个线程并发执行任务,最后需要在所有任务执行完成后再做操作.这里我们就可以使用GCD中的调度组来实现:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"调度组开始执行!!!");
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread1 -> %d", i);
}
});
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread2 -> %d", i);
}
});
dispatch_group_async(group, queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"thread3 -> %d", i);
}
});
dispatch_group_notify(group, queue, ^{
NSLog(@"所有循环执行完毕!!!");
});
打印日志如下:
2018-02-01 17:33:33.687543+0800 Thread[63088:367476] 调度组开始执行!!!
2018-02-01 17:33:33.687879+0800 Thread[63088:367562] thread1 -> 0
2018-02-01 17:33:33.687882+0800 Thread[63088:367553] thread2 -> 0
2018-02-01 17:33:33.687888+0800 Thread[63088:367558] thread3 -> 0
2018-02-01 17:33:33.688551+0800 Thread[63088:367562] thread1 -> 1
2018-02-01 17:33:33.688569+0800 Thread[63088:367553] thread2 -> 1
2018-02-01 17:33:33.688671+0800 Thread[63088:367558] thread3 -> 1
2018-02-01 17:33:33.688963+0800 Thread[63088:367553] thread2 -> 2
2018-02-01 17:33:33.688982+0800 Thread[63088:367562] thread1 -> 2
2018-02-01 17:33:33.689465+0800 Thread[63088:367558] thread3 -> 2
2018-02-01 17:33:33.689706+0800 Thread[63088:367553] thread2 -> 3
2018-02-01 17:33:33.690041+0800 Thread[63088:367562] thread1 -> 3
2018-02-01 17:33:33.690279+0800 Thread[63088:367558] thread3 -> 3
2018-02-01 17:33:33.691043+0800 Thread[63088:367553] thread2 -> 4
2018-02-01 17:33:33.691211+0800 Thread[63088:367562] thread1 -> 4
2018-02-01 17:33:33.691389+0800 Thread[63088:367558] thread3 -> 4
2018-02-01 17:33:33.691583+0800 Thread[63088:367553] thread2 -> 5
2018-02-01 17:33:33.691826+0800 Thread[63088:367562] thread1 -> 5
2018-02-01 17:33:33.692019+0800 Thread[63088:367558] thread3 -> 5
2018-02-01 17:33:33.692796+0800 Thread[63088:367558] 所有循环执行完毕!!!
总结:所有任务执行完成后的操作在 dispatch_group_notify 中写代码即可.
- 但是这里有一个问题:队列中的任务代码是一段for循环,for循环是同步执行的.如果代码块中的任务是异步执行的,还会是现在的效果吗?
- 比如: 我要发起3个异步的网络请求,需要等到3个请求都有响应后再做下一步操作.此时我们仍然使用上面的代码是否能实现呢?
- 这里我用延时操作来模拟异步网络请求延时效果.
- (void)group_asynBlock {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"group 开始执行 !!!");
dispatch_group_async(group, queue, ^{
NSLog(@"block1 begin !!!");
[self request:1];
NSLog(@"block1 end !!!");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block2 begin !!!");
[self request:2];
NSLog(@"block2 end !!!");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block3 begin !!!");
[self request:3];
NSLog(@"block3 end !!!");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"group 结束 !!!");
});
}
- (void)request:(NSInteger)count {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"请求%ld 开始 !!!", count);
[NSThread sleepForTimeInterval:3];
NSLog(@"请求%ld 结束 !!!", count);
});
}
下面我可们可以来猜测一下日志会是怎样的.
我们所期望的是这样的
group 开始执行 !!!
block1 begin !!!
block2 begin !!!
block3 begin !!!
block1 end !!!
block2 end !!!
block3 end !!!
请求2 开始 !!!
请求1 开始 !!!
请求3 开始 !!!
请求2 结束 !!!
请求1 结束 !!!
请求3 结束 !!!
group 结束 !!!
但是实际日志是这样的:
2018-02-01 17:55:42.008339+0800 Thread[67540:402087] group 开始执行 !!!
2018-02-01 17:55:42.008609+0800 Thread[67540:402136] block1 begin !!!
2018-02-01 17:55:42.008619+0800 Thread[67540:402132] block2 begin !!!
2018-02-01 17:55:42.008631+0800 Thread[67540:402133] block3 begin !!!
2018-02-01 17:55:42.009244+0800 Thread[67540:402136] block1 end !!!
2018-02-01 17:55:42.009277+0800 Thread[67540:402132] block2 end !!!
2018-02-01 17:55:42.009280+0800 Thread[67540:402134] 请求1 开始 !!!
2018-02-01 17:55:42.009309+0800 Thread[67540:402135] 请求2 开始 !!!
2018-02-01 17:55:42.009444+0800 Thread[67540:402133] block3 end !!!
2018-02-01 17:55:42.009456+0800 Thread[67540:402136] 请求3 开始 !!!
2018-02-01 17:55:42.010551+0800 Thread[67540:402133] group 结束 !!!
2018-02-01 17:55:45.013680+0800 Thread[67540:402135] 请求2 结束 !!!
2018-02-01 17:55:45.013673+0800 Thread[67540:402134] 请求1 结束 !!!
2018-02-01 17:55:45.014906+0800 Thread[67540:402136] 请求3 结束 !!!
可以看到,group执行结束代码的时机是不确定的,这里请求还没结束,group notify你面的代码却已经执行了.
下面来说下group notify 什么时候会调用:
当所有的group中的代码块中的代码从上至下执行完成后就会调用notify.所以如果代码块中都是同步执行的还好,如果出现了异步执行的情况,因为即使是异步,当前代码块中的代码仍然会往下继续执行.所以无论异步执行的任务是否完成并不能起到决定作用.于是就出现了上面的问题.
那我们如何来解决group中存在异步执行代码的情况呢.
此时需用到 dispatch_group_enter() 和 dispatch_group_leave()
上面代码需要做如下修改
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"group 开始执行 !!!");
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block1 begin !!!");
[self request:1 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block1 end !!!");
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block2 begin !!!");
[self request:2 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block2 end !!!");
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"block3 begin !!!");
[self request:3 block:^{
dispatch_group_leave(group);
}];
NSLog(@"block3 end !!!");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"group 结束 !!!");
});
- (void)request:(NSInteger)count block:(void(^)(void))complete{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"请求%ld 开始 !!!", count);
[NSThread sleepForTimeInterval:3];
NSLog(@"请求%ld 结束 !!!", count);
if(complete)
complete();
});
}
日志如下:
2018-04-04 10:57:03.655066+0800 Thread[13280:128963] group 开始执行 !!!
2018-04-04 10:57:03.655381+0800 Thread[13280:129076] block1 begin !!!
2018-04-04 10:57:03.655385+0800 Thread[13280:129085] block2 begin !!!
2018-04-04 10:57:03.655445+0800 Thread[13280:129078] block3 begin !!!
2018-04-04 10:57:03.657045+0800 Thread[13280:129076] block1 end !!!
2018-04-04 10:57:03.657073+0800 Thread[13280:129079] 请求1 开始 !!!
2018-04-04 10:57:03.657078+0800 Thread[13280:129085] block2 end !!!
2018-04-04 10:57:03.657113+0800 Thread[13280:129078] block3 end !!!
2018-04-04 10:57:03.657184+0800 Thread[13280:129076] 请求2 开始 !!!
2018-04-04 10:57:03.657296+0800 Thread[13280:129085] 请求3 开始 !!!
2018-04-04 10:57:06.659375+0800 Thread[13280:129085] 请求3 结束 !!!
2018-04-04 10:57:06.659375+0800 Thread[13280:129076] 请求2 结束 !!!
2018-04-04 10:57:06.659403+0800 Thread[13280:129079] 请求1 结束 !!!
2018-04-04 10:57:06.659753+0800 Thread[13280:129076] group 结束 !!!
如上,结果和我们所期望的一致.在每一个异步任务开始之前,我们需要调用dispatch_group_enter(group)来防止block块内代码执行完毕后,退出group.然后在异步请求响应后的回调里调用dispatch_group_leave(group)使其正常离开group就ok了.
dispatch_group_enter()和dispatch_group_leave()需要成对出现
GCD 栅栏方法:dispatch_barrier_async
- 一般使用场景,我们开启了多个异步任务.但是需要将其分组,并控制每组的执行顺序的时候可以使用该api
- (void)barrier {
NSInteger count = 3;
dispatch_queue_t queue = dispatch_queue_create("queue_label", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread1 --> %d", i);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread2 --> %d", i);
}
});
dispatch_barrier_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"barrier --> %d", i);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < count; i++) {
NSLog(@"Thread3 --> %d", i);
}
});
}
日志如下:
2018-04-04 14:38:29.095615+0800 Thread[15169:299036] Thread2 --> 0
2018-04-04 14:38:29.095611+0800 Thread[15169:298974] Thread1 --> 0
2018-04-04 14:38:29.095876+0800 Thread[15169:299036] Thread2 --> 1
2018-04-04 14:38:29.095876+0800 Thread[15169:298974] Thread1 --> 1
2018-04-04 14:38:29.096505+0800 Thread[15169:299036] Thread2 --> 2
2018-04-04 14:38:29.096861+0800 Thread[15169:298974] Thread1 --> 2
2018-04-04 14:38:29.097109+0800 Thread[15169:298974] barrier --> 0
2018-04-04 14:38:29.097212+0800 Thread[15169:298974] barrier --> 1
2018-04-04 14:38:29.097304+0800 Thread[15169:298974] barrier --> 2
2018-04-04 14:38:29.097414+0800 Thread[15169:298974] Thread3 --> 0
2018-04-04 14:38:29.097504+0800 Thread[15169:298974] Thread3 --> 1
2018-04-04 14:38:29.097908+0800 Thread[15169:298974] Thread3 --> 2
可以看出barrier之前的任务会先执行,barrier之后的代码会后执行.barrier顾名思义起到了一个分割的作用.
GCD挂起和恢复
- dispatch_suspend()挂起某一个队列,并不是真正意义上的停止,已经开始执行的代码块是不会停止的.只会停止执行还未执行的block代码块
- dispatch_resume()恢复队列任务的执行.和suspend成对出现.
- (void)suspend {
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一个block,延时5秒打印。
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"五秒后打印,队列挂起时已经开始执行,");
});
//提交第二个block,也是延时5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"队列挂起时未执行,需恢复队列后在执行");
});
//延时一秒
NSLog(@"立刻打印~~~~~~~");
[NSThread sleepForTimeInterval:1];
//挂起队列
NSLog(@"一秒后打印,队列挂起");
dispatch_suspend(queue);
//延时10秒
[NSThread sleepForTimeInterval:11];
NSLog(@"十秒后打印,开启队列");
//恢复队列
dispatch_resume(queue);
}
日志如下:
2018-04-04 16:46:12.574933+0800 Thread[17344:474988] 立刻打印~~~~~~~
2018-04-04 16:46:13.576395+0800 Thread[17344:474988] 一秒后打印,队列挂起
2018-04-04 16:46:17.576697+0800 Thread[17344:475108] 五秒后打印,队列挂起时已经开始执行,
2018-04-04 16:46:24.577738+0800 Thread[17344:474988] 十秒后打印,开启队列
2018-04-04 16:46:29.583528+0800 Thread[17344:475104] 队列挂起时未执行,需恢复队列后在执行
GCD 快速迭代方法:dispatch_apply
类似for循环,可以用来遍历
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zd", index);
});
NSLog(@"遍历结束 !!!");
}
日志如下:
2018-04-04 17:11:42.159210+0800 Thread[18256:512357] 1
2018-04-04 17:11:42.159218+0800 Thread[18256:512356] 2
2018-04-04 17:11:42.159241+0800 Thread[18256:512358] 3
2018-04-04 17:11:42.159210+0800 Thread[18256:512257] 0
2018-04-04 17:11:42.159453+0800 Thread[18256:512357] 4
2018-04-04 17:11:42.159453+0800 Thread[18256:512358] 5
2018-04-04 17:11:42.159454+0800 Thread[18256:512356] 6
2018-04-04 17:11:42.159465+0800 Thread[18256:512257] 7
2018-04-04 17:11:42.159568+0800 Thread[18256:512357] 8
2018-04-04 17:11:42.159741+0800 Thread[18256:512358] 9
2018-04-04 17:11:42.160506+0800 Thread[18256:512257] 遍历结束 !!!
- 这里不是有序遍历是因为,我们上面创建的是一个全局队列,是异步执行的.所以顺序是随机的.如果想要顺序遍历,可以创建一个串行队列.