今天下午就和GCD扛上了, 索性好好了解下。 原理及源码不讲, 有兴趣的小伙伴本身去看吧。实际上是我看不懂。(先跑了)。。 今天主要说说平时的使用。。。 开篇两个问题: dispatch_get_global_queue 与 dispatch_get_main_queue 什么区别?? 串行和并行, 异步同步??? Dispatch Barrier
与dispatch_group_wait
有啥区别??? 懵逼啊。。一步一步来看看吧。。。ios
Grand Central Dispatch(GCD)是Apple推出的一套多线程解决方案,它拥有系统级的线程管理机制,开发者不须要再管理线程的生命周期,只须要关注于要执行的任务便可.数据库
dispatch_queue
操做是在多线程上仍是单线程主要是看队列的类型和执行方法,并行队列异步执行才能在多线程,并行队列同步执行就只会在主线程执行了. dispatch_get_global_queue
: //全局队列,一个并行的队列 dispatch_get_main_queue
: //主队列,主线程中的惟一队列,一个串行队列 dispatch_get_global_queue
:用于获取一个全局队列. dispatch_get_main_queue
:该API的使用主要是在更新UI时获取dispatch_get_main_queue()并把任务提交到主队列中. main queue设置了并发数为1,即串行队列,而且将targetq指向com.apple.root.default-overcommit-priority队列。安全
队列优先级有八个,分别为低、默认、高、后台以及对应的overcommit。枚举定义以下:bash
enum {
DISPATCH_ROOT_QUEUE_IDX_LOW_PRIORITY = 0, //低优先级
DISPATCH_ROOT_QUEUE_IDX_LOW_OVERCOMMIT_PRIORITY, //低优先级+overcommit
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY, //默认优先级
DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY, //默认优先级+overcommit
DISPATCH_ROOT_QUEUE_IDX_HIGH_PRIORITY, //高优先级
DISPATCH_ROOT_QUEUE_IDX_HIGH_OVERCOMMIT_PRIORITY, //高优先级+overcommit
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_PRIORITY, //后台
DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_OVERCOMMIT_PRIORITY, //后台+overcomit
};
复制代码
自定义队列:网络
//串行队列
dispatch_queue_create("com.starming.serialqueue", DISPATCH_QUEUE_SERIAL)
//并行队列
dispatch_queue_create("com.starming.concurrentqueue", DISPATCH_QUEUE_CONCURRENT)
复制代码
当咱们处理耗时操做时,好比读取数据库、请求网络数据,为了不这些耗时操做卡住UI,可将耗时任务放到子线程中,执行完成后再通知主线程更新UI,代码示例以下:多线程
//耗时操做
dispatch_async(dispatch_get_main_queue(), ^{
//更新UI
});
});
复制代码
会确保队列中先于Barrier Block提交的任务都完成后再执行它,而且执行时队列不会同步执行其它任务,等Barrier Block执行完成后再开始执行其余任务。 当多线程并发读写同一个资源时,为了保证资源读写的正确性,能够用Barrier Block解决该问题。代码示例以下:并发
//建立自定义并行队列
dispatch_queue_t queue = dispatch_queue_create("com.gcdTest.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//读操做
});
dispatch_barrier_async(queue, ^{
//barrier block,可用于写操做
//确保资源更新过程当中不会有其余线程读取
NSLog(@"work2");
});
dispatch_async(queue, ^{
//读操做
NSLog(@"work3");
});
复制代码
这里有个须要注意也是官方文档上提到的一点,若是咱们调用dispatch_barrier_async时将Barrier blocks提交到一个global queue,barrier blocks执行效果与dispatch_async()一致;只有将Barrier blocks提交到使用DISPATCH_QUEUE_CONCURRENT属性建立的并行queue时它才会表现的如同预期。app
dispatch_group_wait
的使用举例:less
dispatch_queue_t defultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t requestGroup = dispatch_group_create();
dispatch_group_async(requestGroup, defultQueue, ^{
sleep(2);
NSLog(@"++++++++1111");
});
dispatch_group_async(requestGroup, defultQueue, ^{
NSLog(@"+++++++++2222");
});
NSLog(@"++++++++please wait 1 2 \n");
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
NSLog(@"++++++++++task 1 2 finished \n");
dispatch_group_async(requestGroup, defultQueue, ^{
NSLog(@"+++++++++333");
});
dispatch_group_async(requestGroup, defultQueue, ^{
NSLog(@"+++++++++4444");
});
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
NSLog(@"+++++++++task 3 4 finished \n");
复制代码
打印结果:异步
2019-05-22 20:39:46.759798+0800 GearBest[84446:1389970] ++++++++please wait 1 2
2019-05-22 20:39:46.759844+0800 GearBest[84446:1390248] +++++++++2222
2019-05-22 20:39:46.759839+0800 GearBest[84446:1390277] ++++++++1111
2019-05-22 20:39:46.760051+0800 GearBest[84446:1389970] ++++++++++task 1 2 finished
2019-05-22 20:39:46.760182+0800 GearBest[84446:1390248] +++++++++4444
2019-05-22 20:39:46.760173+0800 GearBest[84446:1390277] +++++++++333
2019-05-22 20:39:46.760338+0800 GearBest[84446:1389970] +++++++++task 3 4 finished
复制代码
以上即便加了睡眠,也是能够保证先执行12,完成以后在执行34的。
我用Dispatch Barrier也实现了上述功能,代码以下:
dispatch_queue_t defultQueue = dispatch_queue_create("testGCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(defultQueue, ^{
sleep(2);
NSLog(@"++++++++1111");
});
dispatch_async(defultQueue, ^{
NSLog(@"+++++++++2222");
});
dispatch_barrier_async(defultQueue, ^{
NSLog(@"++++++++++task 1 2 finished \n");
});
dispatch_async(defultQueue, ^{
NSLog(@"+++++++++333");
});
dispatch_async(defultQueue, ^{
NSLog(@"+++++++++4444");
});
dispatch_barrier_async(defultQueue, ^{
NSLog(@"+++++++++task 3 4 finished \n");
});
复制代码
打印结果:
2019-05-22 20:49:51.266512+0800 GearBest[85906:1403037] +++++++++2222
2019-05-22 20:49:53.266866+0800 GearBest[85906:1403000] ++++++++1111
2019-05-22 20:49:53.267081+0800 GearBest[85906:1403000] ++++++++++task 1 2 finished
2019-05-22 20:49:53.267217+0800 GearBest[85906:1403036] +++++++++4444
2019-05-22 20:49:53.267217+0800 GearBest[85906:1403000] +++++++++333
2019-05-22 20:49:53.267339+0800 GearBest[85906:1403036] +++++++++task 3 4 finished
复制代码
由上面得出的结论:Dispatch Barrier 和 dispatch_group_wait 能够实现相同的效果,只是写法不一样。
// 注意这里mainqueue 不行。
dispatch_queue_t globaleQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globaleQueue, ^{
dispatch_apply(5, globaleQueue, ^(size_t index) {
NSLog(@"11111");
});
NSLog(@"apply");
});
复制代码
结果:
2019-05-27 23:21:12.505513+0800 TestCode[3782:396214] 11111
2019-05-27 23:21:12.505513+0800 TestCode[3782:396192] 11111
2019-05-27 23:21:12.505513+0800 TestCode[3782:396194] 11111
2019-05-27 23:21:12.505550+0800 TestCode[3782:396193] 11111
2019-05-27 23:21:12.505768+0800 TestCode[3782:396214] 11111
2019-05-27 23:21:12.506075+0800 TestCode[3782:396214] apply
复制代码
? 直接用dispatch_group实现相似的功能:顺序执行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1111111");
});
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"2222222");
});
dispatch_group_async(group, queue, ^{
NSLog(@"33333333");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"都执行完了啊");
});
复制代码
打印结果:
2019-05-27 22:39:23.734921+0800 TestCode[2272:267297] 33333333
2019-05-27 22:39:23.734921+0800 TestCode[2272:267302] 1111111
2019-05-27 22:39:25.737724+0800 TestCode[2272:267300] 2222222
2019-05-27 22:39:25.738355+0800 TestCode[2272:267300] 都执行完了啊
复制代码
dispatch_async
用来异步执行任务.dispatch_async封装调用了dispatch_async_f函数,先将block拷贝到堆上,避免block执行前被销毁,同时传入_dispatch_call_block_and_release来保证block执行后会执行Block_release。
总结一下:dispatch_async的流程是用链表保存全部提交的block,而后在底层线程池中,依次取出block并执行;而向主队列提交block则会向主线程的Runloop发送消息并唤醒Runloop,接着会在回调函数中取出block并执行。
dispatch_sync
的逻辑主要是将任务放入队列,并用线程专属信号量作等待,保证每次只会有一个block在执行。
稍有不慎 就会死锁; 例如:
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello world?");
});
//仍是死锁。。 自建立queue 这样也是死锁。。
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello world?");
});
});
复制代码
主线程中执行的block 等待主线程中执行的block执行结束。 因此死锁了。
dispatch_group
能够将GCD的任务合并到一个组里来管理,也能够同时监听组里全部任务的执行状况. 实际使用状况举例:有多个网络请求,请求一、2完成拿到结果以后,才能够进行后面的请求。这时候就可使用dispatch_group。 栗子代码:
dispatch_group_t requestGroup = dispatch_group_create();
dispatch_group_enter(requestGroup);
[self loadHeadDataCompletion:^{
//请求1
NSLog(@"+++++++111111");
dispatch_group_leave(requestGroup);
}];
dispatch_group_enter(requestGroup);
[self requestRecommandSearchAdvertiseDataCompletion:^{
//请求2
NSLog(@"+++++++2222");
dispatch_group_leave(requestGroup);
}];
dispatch_group_notify(requestGroup, dispatch_get_main_queue(), ^{
//拿到了请求12的*结果*以后,在进行请求3
[self loadHomeGoodsList];
});
复制代码
dispatch_group有两个须要注意的地方: 一、dispatch_group_enter必须在dispatch_group_leave以前出现 二、dispatch_group_enter和dispatch_group_leave必须成对出现
dispatch_group本质是个初始值为LONG_MAX的信号量,等待group中的任务完成实际上是等待value恢复初始值。 dispatch_group_enter和dispatch_group_leave必须成对出现。 若是dispatch_group_enter比dispatch_group_leave多一次,则wait函数等待的 线程不会被唤醒和注册notify的回调block不会执行; 若是dispatch_group_leave比dispatch_group_enter多一次,则会引发崩溃。
dispatch_once
能保证任务只会被执行一次,即便同时多线程调用也是线程安全的。经常使用于建立单例、swizzeld method等功能. dispatch_once用原子性操做block执行完成标记位,同时用信号量确保只有一个线程执行block,等block执行完再唤醒全部等待中的线程。
dispatch_source
主要用于定时器,比NSTimer精度高一点点吧。 其余计时器参考:NSTimer NSTimer 到底准不许