GCD基础 iOS
1. dispatch_after
该函数用于任务延时执行,其中参数dispatch_time_t代表延时时长,dispatch_queue_t代表使用哪个队列。如果队列为主队列,那么任务在主线程执行,如果队列为全局队列或者自己创建的队列,那么任务在子线程执行,代码如下:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",[NSThread currentThread]); });
打印结果:
2021-05-31 09:33:23.721507+0800 GCDTest[37849:5800498] <NSThread: 0x6000005b0a40>{number = 1, name = main}
2. dispatch_once
保证函数在整个生命周期内只会执行一次,看代码。
//dispatch_once 保证函数在整个生命周期内只会执行一次, -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"%@",[NSThread currentThread]); }); }
打印结果:
3. dispatch_group_async & dispatch_group_notify
并发异步下载多张图片然后拼接为一张,用GCD来实现,有一个神器的东西叫做队列组,当加入到队列组中的所有任务执行完成之后,会调用dispatch_group_notify函数通知任务全部完成,代码如下:
dispatch_group_t group = dispatch_group_create(); __block UIImage *image_1 = nil; __block UIImage *image_2 = nil; //在group中添加一个任务 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ image_1 = [self imageWithPath:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502706256731&di=371f5fd17184944d7e2b594142cd7061&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201605%2F14%2F20160514165210_LRCji.jpeg"]; }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ image_2 = [self imageWithPath:@"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=776127947,2002573948&fm=26&gp=0.jpg"]; }); //group中所有任务执行完毕,通知该方法执行 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0f); [image_2 drawInRect:CGRectMake(0, 0, 100, 100)]; [image_1 drawInRect:CGRectMake(100, 0, 100, 100)]; UIImage *image_3 = UIGraphicsGetImageFromCurrentImageContext(); //image_3 即合并后的图片 UIGraphicsEndImageContext(); });
4. dispatch_barrier_async
栅栏函数,使用此方法创建的任务,会查找当前队列中有没有其他任务要执行,如果有,则等待已有任务执行完毕后再执行,同时,在此任务之后进入队列的任务,需要等待此任务执行完成后,才能执行。
dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务1"); }); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务2"); }); dispatch_barrier_async(self.concurrentQueue, ^{ NSLog(@"任务barrier"); }); NSLog(@"big"); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务3"); }); NSLog(@"apple"); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务4"); });
运行结果:
2021-05-31 09:29:39.579136+0800 GCDTest[37772:5795806] big 2021-05-31 09:29:39.579144+0800 GCDTest[37772:5797090] 任务1 2021-05-31 09:29:39.579150+0800 GCDTest[37772:5797092] 任务2 2021-05-31 09:29:39.579252+0800 GCDTest[37772:5795806] apple 2021-05-31 09:29:39.579298+0800 GCDTest[37772:5797092] 任务barrier 2021-05-31 09:29:39.579387+0800 GCDTest[37772:5797092] 任务3 2021-05-31 09:29:39.579395+0800 GCDTest[37772:5797090] 任务4
分析:
不要着急,我们换一下函数:
dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务1"); }); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务2"); }); dispatch_barrier_sync(self.concurrentQueue, ^{ NSLog(@"任务barrier"); }); NSLog(@"big"); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务3"); }); NSLog(@"apple"); dispatch_async(self.concurrentQueue, ^{ NSLog(@"任务4"); });
打印结果:
2021-05-31 09:30:09.860632+0800 GCDTest[37807:5798023] 任务1 2021-05-31 09:30:09.860651+0800 GCDTest[37807:5798027] 任务2 2021-05-31 09:30:09.860788+0800 GCDTest[37807:5797935] 任务barrier 2021-05-31 09:30:09.860882+0800 GCDTest[37807:5797935] big 2021-05-31 09:30:09.860984+0800 GCDTest[37807:5797935] apple 2021-05-31 09:30:09.861007+0800 GCDTest[37807:5798027] 任务3 2021-05-31 09:30:09.861107+0800 GCDTest[37807:5798024] 任务4
这两个函数对于队列的栅栏作用是一样的,但是对于该函数相对于其他内部函数遵循了最开始说到的同步和异步的规则。
5. dispatch_apply 并发队列快速迭代函数
通常我们会用 for 循环遍历,但是 GCD 给我们提供了快速迭代的方法 dispatch_apply。dispatch_apply 按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。
如果是在串行队列中使用 dispatch_apply,那么就和 for 循环一样,按顺序同步执行。但是这样就体现不出快速迭代的意义了。
我们可以利用并发队列进行异步执行。比如说遍历 0~5 这 6 个数字,for 循环的做法是每次取出一个元素,逐个遍历。dispatch_apply 可以 在多个线程中同时(异步)遍历多个数字。
还有一点,无论是在串行队列,还是并发队列中,dispatch_apply 都会等待全部任务执行完毕,这点就像是同步操作,也像是队列组中的 dispatch_group_wait方法。
串行队列:
dispatch_apply(5, self.serialQueue, ^(size_t i) { NSLog(@"第%@次_%@",@(i),[NSThread currentThread]); });
运行结果:
2021-05-28 17:40:47.966315+0800 GCDTest[34039:5594177] 第0次_<NSThread: 0x600000eb41c0>{number = 1, name = main} 2021-05-28 17:40:47.966474+0800 GCDTest[34039:5594177] 第1次_<NSThread: 0x600000eb41c0>{number = 1, name = main} 2021-05-28 17:40:47.966591+0800 GCDTest[34039:5594177] 第2次_<NSThread: 0x600000eb41c0>{number = 1, name = main} 2021-05-28 17:40:47.966713+0800 GCDTest[34039:5594177] 第3次_<NSThread: 0x600000eb41c0>{number = 1, name = main} 2021-05-28 17:40:47.966820+0800 GCDTest[34039:5594177] 第4次_<NSThread: 0x600000eb41c0>{number = 1, name = main}
并行队列:
dispatch_apply(5, self.concurrentQueue, ^(size_t i) { NSLog(@"第%@次_%@",@(i),[NSThread currentThread]); });
2021-05-28 17:42:41.627885+0800 GCDTest[34068:5596872] 第2次_<NSThread: 0x600002f10a00>{number = 6, name = (null)} 2021-05-28 17:42:41.627887+0800 GCDTest[34068:5596745] 第0次_<NSThread: 0x600002f501c0>{number = 1, name = main} 2021-05-28 17:42:41.627892+0800 GCDTest[34068:5596874] 第4次_<NSThread: 0x600002f16540>{number = 8, name = (null)} 2021-05-28 17:42:41.627891+0800 GCDTest[34068:5596873] 第3次_<NSThread: 0x600002f55940>{number = 5, name = (null)} 2021-05-28 17:42:41.627895+0800 GCDTest[34068:5596878] 第1次_<NSThread: 0x600002f14940>{number = 7, name = (null)}
6. dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait
看这几个函数的时候你需要抛开队列,丢掉同步异步,不要把它们想到一起,混为一谈,信号量只是控制任务执行的一个条件而已,相对于上面通过队列以及执行方式来控制线程的开辟和任务的执行,它更贴近对于任务直接的控制。类似于单个队列的最大并发数的控制机制,提高并行效率的同时,也防止太多线程的开辟对CPU早层负面的效率负担。
dispatch_semaphore_create创建信号量,初始值不能小于0;
dispatch_semaphore_wait等待降低信号量,也就是信号量-1;
dispatch_semaphore_signal提高信号量,也就是信号量+1;
dispatch_semaphore_wait和dispatch_semaphore_signal通常配对使用。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); for (int i=0; i<5; i++) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_async(self.concurrentQueue, ^{ NSLog(@"第%@次_%@",@(i),[NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); }
运行结果
2021-05-28 16:51:21.334126+0800 GCDTest[33586:5561922] 第0次_<NSThread: 0x6000026ef8c0>{number = 6, name = (null)} 2021-05-28 16:51:21.334389+0800 GCDTest[33586:5561922] 第1次_<NSThread: 0x6000026ef8c0>{number = 6, name = (null)} 2021-05-28 16:51:21.334579+0800 GCDTest[33586:5561922] 第2次_<NSThread: 0x6000026ef8c0>{number = 6, name = (null)} 2021-05-28 16:51:21.334738+0800 GCDTest[33586:5561922] 第3次_<NSThread: 0x6000026ef8c0>{number = 6, name = (null)} 2021-05-28 16:51:21.334892+0800 GCDTest[33586:5561922] 第4次_<NSThread: 0x6000026ef8c0>{number = 6, name = (null)}