级别: ★★☆☆☆
作者: dac_1033 https://www.jianshu.com/u/455ee5a3828a
审校: QiShare 团队 https://www.jianshu.com/c/b3bd94559163
一, GCD 简介
Grand Central Dispatch(GCD)是由苹果开发的多线程调度框架, 能够优化多线程应用程序的执行过程以支持多核处理器. GCD 底层有线程池, 线程池的概念在上一篇文章中有简单介绍, 线程池是系统自动来维护, 开发者只关注队列 (Dispatch Queue) 及任务的创建和同步异步调用即可. iOS 中使用 GCD 来实现线程操作很简单: 创建队列> 同步 / 异步调用> 将任务放在上一步调用的 block 中. 下面我们来实现异步执行耗时操作, 当耗时操作执行完毕时, 回到主线程来更新相应的 UI 的功能, 简易的代码如下:
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_async(queue, ^{
- // 放一些极其耗时间的任务在此执行
- dispatch_async(dispatch_get_main_queue(), ^{
- // 耗时任务完成, 拿到资源, 更新 UI 更新 UI 只可以在主线程中更新
- });
- });
在调用 GCD 中方法执行多线程操作时, GCD 可自动利用 CPU 的多核实现异步处理任务, 自动管理线程的生命周期(创建线程, 调度任务, 销毁线程), 开发者只需要设置 GCD 需要执行的任务, 不用编写任何线程管理代码. 因此 GCD 也是苹果最为推荐开发者使用的多线程框架.
二, GCD 详解
首先我们使用 GCD 实现异步加载多张网络图片, 来熟悉一下 GCD 的使用过程:
- //// 自定义图片 Model
- @interface GCDImage : NSObject
- @property (nonatomic, assign) NSInteger index;
- @property (nonatomic, strong) NSData *imgData;
- @end
- @implementation GCDImage
- @end
- //// GCD 加载一张网络图片
- #define ColumnCount 4
- #define RowCount 5
- #define Margin 10
- @interface MultiThread_GCD ()
- @property (nonatomic, strong) NSMutableArray *imageViews;
- @end
- @implementation MultiThread_GCD
- - (void)viewDidLoad {
- [super viewDidLoad];
- [self setTitle:@"GCD"];
- [self.view setBackgroundColor:[UIColor whiteColor]];
- self.edgesForExtendedLayout = UIRectEdgeNone;
- [self layoutViews];
- }
- - (void)layoutViews {
- CGSize size = self.view.frame.size;
- CGFloat imgWidth = (size.width - Margin * (ColumnCount + 1)) / ColumnCount;
- _imageViews=[NSMutableArray array];
- for (int row=0; row<RowCount; row++) {
- for (int colomn=0; colomn<ColumnCount; colomn++) {
- UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(Margin + colomn * (imgWidth + Margin), Margin + row * (imgWidth + Margin), imgWidth, imgWidth)];
- imageView.backgroundColor = [UIColor cyanColor];
- [self.view addSubview:imageView];
- [_imageViews addObject:imageView];
- }
- }
- UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
- button.frame = CGRectMake(15, (imgWidth + Margin) * RowCount + Margin, size.width - 15 * 2, 45);
- [button addTarget:self action:@selector(loadImageWithMultiOperation) forControlEvents:UIControlEventTouchUpInside];
- [button setTitle:@"点击加载" forState:UIControlStateNormal];
- [self.view addSubview:button];
- }
- #pragma mark - 多线程下载图片
- //- (void)loadImageWithMultiOperation {
- //
- // int count = RowCount * ColumnCount;
- //
- // // 创建一个串行队列 第一个参数: 队列名称 第二个参数: 队列类型
- // // 注意 queue 对象不是指针类型
- // dispatch_queue_t serialQueue=dispatch_queue_create("QiShareSerialQueue", DISPATCH_QUEUE_SERIAL);
- // // 创建多个线程用于填充图片
- // for (int i=0; i<count; ++i) {
- // // 异步执行队列任务
- // dispatch_async(serialQueue, ^{
- // GCDImage *gcdImg = [[GCDImage alloc] init];
- // gcdImg.index = i;
- // [self loadImg:gcdImg];
- // });
- //
- // }
- //}
- - (void)loadImageWithMultiOperation {
- int count = RowCount * ColumnCount;
- // 取得全局队列 第一个参数: 线程优先级 第二个参数: 标记参数, 目前没有用, 一般传入 0
- dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- // 创建多个线程用于填充图片
- for (int i=0; i<count; ++i) {
- // 异步执行队列任务
- dispatch_sync(globalQueue, ^{
- GCDImage *gcdImg = [[GCDImage alloc] init];
- gcdImg.index = i;
- [self loadImg:gcdImg];
- });
- }
- }
- #pragma mark - 加载图片
- - (void)loadImg:(GCDImage *)gcdImg {
- // 请求数据
- gcdImg.imgData = [self requestData];
- // 更新 UI 界面(mainQueue 是 UI 主线程
- // dispatch_sync(dispatch_get_main_queue(), ^{
- [self updateImage:gcdImg];
- // });
- // 打印当前线程
- NSLog(@"current thread: %@", [NSThread currentThread]);
- }
- #pragma mark - 请求图片数据
- - (NSData *)requestData {
- NSURL *url = [NSURL URLWithString:@"https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/image/AppleInc/aos/published/images/a/pp/apple/products/apple-products-section1-one-holiday-201811?wid=2560&hei=1046&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1540576114151"];
- NSData *data = [NSData dataWithContentsOfURL:url];
- return data;
- }
- #pragma mark - 将图片显示到界面
- - (void)updateImage:(GCDImage *)gcdImg {
- UIImage *image = [UIImage imageWithData:gcdImg.imgData];
- UIImageView *imageView = _imageViews[gcdImg.index];
- imageView.image = image;
- }
- @end
下面我们就来逐步详细介绍 GCD 相关的细节...
2.1 GCD 中用来执行任务的常用方法
- // 同步执行任务(在当前线程中执行任务, 不会开启新线程)
- dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- // 异步执行任务(可以在新线程中执行任务, 有开启新线程的能力)
- dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
其中, 第一个参数为任务所要加入的队列; 第二个参数是一个 dispatch_block_t 类型的 block, 即任务(一段代码).
2.2 GCD 中的队列
- // 串行队列的创建方法
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
- // 并发队列的创建方法
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
其中, 第一个参数为队列的名称; 第二个参数根据取值命名, 就可看出为队列的类型.
两个特殊的队列:
主队列(Main Dispatch Queue): 是串行队列, 所有放在主队列中的任务, 都会放到主线程中执行; 获取主队列: dispatch_queue_t queue = dispatch_get_main_queue();
全局队列(Global Dispatch Queue): 是并发行队列; 获取全局队列: dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 其中, 第一个参数为队列优先级, 一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT; 第二个参数用 0 即可.
则调度方法与任务所在队列的组合如下:
调度方法 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
异步 (dispatch_async) | 开启多个新线程,并发执行任务 | 开启 1 个新线程,串行执行任务 | 没有开启新线程,串行执行任务 |
同步 (dispatch_sync) | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 | 主线程调用:死锁导致程序卡死 < br ztid="145" ow="0" oh="0"> 其他线程调用:没有开启新线程,串行执行任务 |
我们可以运行下面的代码来验证上列表组合中每一项的正确性, 如下:
- - (void)addTask:(NSInteger)tag {
- [NSThread sleepForTimeInterval:2];
- NSLog(@"addTask%ld--->> %@", (long)tag, [NSThread currentThread]);
- NSURLSessionTask *task = [NSURLSession.sharedSession dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.so.com/"]] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- NSLog(@"任务完成 %ld--->> %@", (long)tag, [NSThread currentThread]);
- }];
- [task resume];
- }
- #pragma mark - 串行 / 并发队列 + 同步 / 异步调用组合
- // 异步执行
- - (void)asyncExecute {
- NSLog(@"CurrentThread---%@", [NSThread currentThread]);
- NSLog(@"asyncExecute start");
- // + 并发队列(开启多个线程, 并发执行任务)
- dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_CONCURRENT);
- // // + 串行队列(开启 1 个新线程, 串行执行任务)
- // dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_SERIAL);
- // // + 主队列(没有开启新线程, 串行执行任务)
- // dispatch_queue_t queue = dispatch_get_main_queue();
- for (NSInteger i=0; i<10; i++) {
- dispatch_async(queue, ^{
- // 追加任务
- [self addTask:i];
- });
- }
- NSLog(@"asyncExecute end");
- }
- // 同步执行
- - (void)syncExecute {
- NSLog(@"currentThread---%@", [NSThread currentThread]);
- NSLog(@"syncExecute start");
- // // + 并发队列(没有开启新线程, 串行执行任务)
- // dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_CONCURRENT);
- // // + 串行队列(没有开启新线程, 串行执行任务)
- // dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_SERIAL);
- // + 主队列(1. 主线程调用: 死锁; 2. 其他线程调用: 不会开启新线程, 执行完一个任务, 再执行下一个任务)
- dispatch_queue_t queue = dispatch_get_main_queue();
- for (NSInteger i=0; i<10; i++) {
- dispatch_sync(queue, ^{
- // 追加任务
- [self addTask:i];
- });
- }
- NSLog(@"syncExecute end");
- }
2.3 GCD 线程间通信
例如, 从非主线程中异步执行完一个操作后, 回到主线程更新 UI 的大致操作如下:
- dispatch_async(dispatch_get_global_queue(0, 0), ^{
- // 如果下载结束回到主线程更新 UI
- dispatch_async(dispatch_get_main_queue(), ^{
- // 更新 UI
- });
- });
2.4 关于 GCD 死锁
在主线程中, 直接执行以下代码:
- dispatch_sync(dispatch_get_main_queue(), ^{
- });
就会界面卡死, 发生 GCD 死锁. 原因就是主线程正在执行 dispatch_sync 操作(同步, 并等待其中 block 完成), 而 dispatch_sync 操作需要等待主线程执行完当前操作才能将 block 加入主队列, 这样就形成了 "互相等待". 通常情况下, 在一个线程正在执行一个串行队列 sQueue 上任务的过程中, 再次调用 dispatch_sync 同步执行这个串行队列 sQueue 在上的任务, 就会引起死锁, 如下:
- - (void)deadLock {
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
- NSLog(@"1");
- dispatch_async(queue, ^{
- NSLog(@"2");
- dispatch_sync(queue, ^{
- NSLog(@"3");
- });
- });
- NSLog(@"4");
- }
- //// 打印日志(界面卡死)
- //2019-01-14 18:07:22.161085+0800 QiMultiThread[469:47692] 1
- //2019-01-14 18:07:22.161286+0800 QiMultiThread[469:47692] 4
- //2019-01-14 18:07:22.161346+0800 QiMultiThread[469:47707] 2
三, GCD 中的其他常用方法
3.1 dispatch_once
一般用来实现创建一个单例, 将某个类的实例化部分放在 dispatch_once 的 block 中来实现.
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // code to be executed once
- })
3.2 dispatch_after
在一个 queue 中, 延时执行某个操作, 比如 App 启动后先执行, 开屏幕动画, 延时加载界面等.
- dispatch_queue_t queue= dispatch_get_main_queue();
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
- // 在 queue 里面延迟执行的一段代码...
- });
3.3 dispatch_apply
用 dispatch_apply 方法可以执行 queue 中的一个指定任务(即 block)n 次.
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_apply(10, queue, ^(size_t i) {
- NSLog(@"执行 第 %lu 次", (long)i);
- });
注意: 如果队列是并发队列, 则会并发执行 block 任务, dispatch_apply 是一个同步调用, block 任务执行 n 次后才返回.
3.4 dispatch_barrier
dispatch_barrier 有两个方法: dispatch_barrier_async 和 dispatch_barrier_sync. 在同一个队列中 dispatch_barrier 方法需要等待其前面所有任务执行完毕, 再执行自己, 自己执行完之后再执行其后面的任务.
- -(void)diapatchBarrier {
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- NSLog(@"---start---");
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_async(queue, ^{
- [NSThread sleepForTimeInterval:7];
- NSLog(@"asyncTask_1");
- });
- dispatch_async(queue, ^{
- [NSThread sleepForTimeInterval:5];
- NSLog(@"asyncTask_2");
- });
- dispatch_barrier_async(queue, ^{
- NSLog(@"barrier_asyncTask");
- [NSThread sleepForTimeInterval:3];
- });
- dispatch_async(queue, ^{
- [NSThread sleepForTimeInterval:1];
- NSLog(@"asyncTask_4");
- });
- NSLog(@"---end---");
- }
- //// 打印日志
- //2019-01-14 16:56:40.673822+0800 QiMultiThread[401:32253] currentThread: <NSThread: 0x1c42622c0>{number = 1, name = main}
- //2019-01-14 16:56:40.674137+0800 QiMultiThread[401:32253] ---start---
- //2019-01-14 16:56:40.674364+0800 QiMultiThread[401:32253] ---end---
- //2019-01-14 16:56:45.681232+0800 QiMultiThread[401:32340] asyncTask_2
- //2019-01-14 16:56:47.682411+0800 QiMultiThread[401:32285] asyncTask_1
- //2019-01-14 16:56:47.682631+0800 QiMultiThread[401:32285] barrier_asyncTask
- //2019-01-14 16:56:51.688996+0800 QiMultiThread[401:32285] asyncTask_4
3.5 dispatch_group
在串行队列中, 如果想让一任务 A 在其他一系列任务 B,C,D 完成之后再执行, 那么在这个串行队列中, 将任务 A 追加在这一些列任务之后就可以了. 但是在并行队列中, 就需要用 dispatch_group 来实现这波操作.
1)dispatch_group_notify
因为 dispatch_group_notify 是异步执行的, 所以不会阻塞当前线程.
- -(void)asyncGroupNotify
- {
- NSLog(@"star");
- dispatch_group_t group=dispatch_group_create();
- dispatch_queue_t queue=dispatch_queue_create("com.GCD_demo.www", DISPATCH_QUEUE_CONCURRENT);
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:1];
- NSLog(@"group_work_1");
- });
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:6];
- NSLog(@"group_work_2");
- });
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:2];
- NSLog(@"group_work_3");
- });
- dispatch_group_notify(group, queue, ^{
- NSLog(@"dispatch_group_Notify 结束");
- });
- }
- //// 打印日志
- //2019-01-14 15:21:15.194094+0800 QiMultiThread[295:9717] ---start---
- //2019-01-14 15:21:15.194270+0800 QiMultiThread[295:9717] ---end---
- //2019-01-14 15:21:17.198617+0800 QiMultiThread[295:9820] groupTask_1
- //2019-01-14 15:21:17.199424+0800 QiMultiThread[295:9820] currentThread: <NSThread: 0x1c4660640>{number = 3, name = (null)}
- //2019-01-14 15:21:19.200911+0800 QiMultiThread[295:9825] groupTask_3
- //2019-01-14 15:21:19.201224+0800 QiMultiThread[295:9825] currentThread: <NSThread: 0x1c0278640>{number = 4, name = (null)}
- //2019-01-14 15:21:22.200290+0800 QiMultiThread[295:9745] groupTask_2
- //2019-01-14 15:21:22.200595+0800 QiMultiThread[295:9745] currentThread: <NSThread: 0x1c4666b00>{number = 5, name = (null)}
- //2019-01-14 15:21:22.200810+0800 QiMultiThread[295:9745] dispatch_group_Notify 结束
2)dispatch_group_wait
顾名思义, dispatch_group_wait 会阻塞当前线程, 直到任务都完成时才会继续执行之后的代码.
- -(void)dispatchGroupWait {
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- NSLog(@"---start---");
- dispatch_group_t group = dispatch_group_create();
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:2];
- NSLog(@"groupTask_1");
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- });
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:7];
- NSLog(@"groupTask_2");
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- });
- dispatch_group_async(group, queue, ^{
- [NSThread sleepForTimeInterval:4];
- NSLog(@"groupTask_3");
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- });
- long result = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC));
- NSLog(@"dispatch_group_wait result = %ld", result);
- NSLog(@"---end---");
- }
- //// 打印日志
- //2019-01-14 15:46:37.226768+0800 QiMultiThread[323:15757] ---start---
- //2019-01-14 15:46:39.231152+0800 QiMultiThread[323:15795] groupTask_1
- //2019-01-14 15:46:39.231367+0800 QiMultiThread[323:15795] currentThread: <NSThread: 0x1c0073600>{number = 3, name = (null)}
- //2019-01-14 15:46:41.232170+0800 QiMultiThread[323:15801] groupTask_3
- //2019-01-14 15:46:41.232644+0800 QiMultiThread[323:15801] currentThread: <NSThread: 0x1c00736c0>{number = 4, name = (null)}
- //2019-01-14 15:46:44.231956+0800 QiMultiThread[323:15827] groupTask_2
- //2019-01-14 15:46:44.232328+0800 QiMultiThread[323:15827] currentThread: <NSThread: 0x1c446b440>{number = 5, name = (null)}
- //2019-01-14 15:46:44.232468+0800 QiMultiThread[323:15757] dispatch_group_wait result = 0
- //2019-01-14 15:46:44.232525+0800 QiMultiThread[323:15757] ---end---
注: dispatch_group_wait 中设置了 10 秒的等待时间, 如果 group 所有任务的执行时间<=10 秒就返回 0, 如果 > 10 秒则返回非 0.
3)dispatch_group_enter 和 dispatch_group_leave
用 dispatch_group_enter 跟 dispatch_group_leave 方法, 并配合 dispatch_async 方法使用, 可以代替 dispatch_group_async. 不过这样操作更显麻烦, dispatch_group_enter 与 dispatch_group_leave 两个方法要配对出现, 且 group 操作结尾仍需要 dispatch_group_notify.
- -(void)dispatchGroupEnter {
- NSLog(@"currentThread: %@", [NSThread currentThread]);
- NSLog(@"---start---");
- dispatch_group_t group = dispatch_group_create();
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_group_enter(group);
- dispatch_async(queue, ^{
- [NSThread sleepForTimeInterval:7];
- NSLog(@"asyncTask_1");
- dispatch_group_leave(group);
- });
- dispatch_group_enter(group);
- dispatch_async(queue, ^{
- [NSThread sleepForTimeInterval:4];
- NSLog(@"asyncTask_2");
- dispatch_group_leave(group);
- });
- dispatch_group_notify(group, dispatch_get_main_queue(), ^{
- NSLog(@"dispatch_group_notify block end");
- });
- NSLog(@"---end---");
- }
- }
- //// 打印日志
- //2019-01-14 15:47:56.818998+0800 QiMultiThread[326:16283] currentThread: <NSThread: 0x1c007bc00>{number = 1, name = main}
- //2019-01-14 15:47:56.819064+0800 QiMultiThread[326:16283] ---start---
- //2019-01-14 15:47:56.819122+0800 QiMultiThread[326:16283] ---end---
- //2019-01-14 15:48:00.824143+0800 QiMultiThread[326:16317] asyncTask_2
- //2019-01-14 15:48:03.824189+0800 QiMultiThread[326:16314] asyncTask_1
- //2019-01-14 15:48:03.824322+0800 QiMultiThread[326:16283] dispatch_group_notify block end
3.6 dispatch_block
如下面将任务 (block) 添加到队列中的两个方法:
- dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
其中, 我们可以把第二个参数单独定义一个 dispatch_block_t 型变量, 再传入相应方法中:
- // 单独定义一个 dispatch_block_t 型变量
- dispatch_block_t block=dispatch_block_create(0, ^{
- NSLog(@"dispatchBlock_work");
- });
- // 调用过程 dispatch_async(queue, block);
1)dispatch_block_wait
dispatch_block_wait 的执行效果与 dispatch_group_wait 类似, 同样会阻塞当前线程. 下面的代码中, 将 wait 操作放在了 dispatch_async 中来执行(且 queue 为 DISPATCH_QUEUE_CONCURRENT 型), 避免了阻塞主线程.
- - (void)dispatchBlockWait {
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_block_t block = dispatch_block_create(0, ^{
- NSLog(@"---before---");
- [NSThread sleepForTimeInterval:7];
- NSLog(@"---after---");
- });
- dispatch_async(queue, block);
- dispatch_async(queue, ^{
- long result = dispatch_block_wait(block, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
- NSLog(@"dispatch_block_wait result = %ld", result);
- });
- }
- //2019-01-14 16:34:59.389940+0800 QiMultiThread[382:27805] ---before---
- //2019-01-14 16:35:02.396144+0800 QiMultiThread[382:27809] dispatch_block_wait result = 49
- //2019-01-14 16:35:06.395332+0800 QiMultiThread[382:27805] ---after---
2)dispatch_block_notify
dispatch_block_notify 当观察的某个 block 执行结束之后立刻通知提交另一特定的 block 到指定的 queue 中执行, 该函数有三个参数, 第一参数是需要观察的 block, 第二个参数是被通知 block 提交执行的 queue, 第三参数是当需要被通知执行的 block
- - (void)dispatchBlockNotify {
- //dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
- dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_block_t preBlock = dispatch_block_create(0, ^{
- NSLog(@"preBlock start");
- [NSThread sleepForTimeInterval:2];
- NSLog(@"preBlock end");
- });
- dispatch_async(queue, preBlock);
- dispatch_block_t afterBlock = dispatch_block_create(0, ^{
- NSLog(@"has been notifyed");
- });
- dispatch_block_notify(preBlock, queue, afterBlock);
- }
- //// 打印日志
- //2019-01-14 17:20:33.893522+0800 QiMultiThread[425:37900] preBlock start
- //2019-01-14 17:20:35.898765+0800 QiMultiThread[425:37900] preBlock end
- //2019-01-14 17:20:35.899008+0800 QiMultiThread[425:37900] has been notifyed
注: 此处 queue 为 DISPATCH_QUEUE_SERIAL 或 DISPATCH_QUEUE_CONCURRENT 均可; 当 preBlock 执行完后, afterBlock 会自动提交到 queue 中执行(queue 可以是其他自定义队列).
3)dispatch_block_cancel 提交到队列的 block, 是可以撤销的, 如下
- dispatch_block_t block = dispatch_block_create(0, ^{
- });
- dispatch_async(queue, block);
- dispatch_block_cancel(block); // 撤销一个任务
3.7 dispatch_set_target_queue
dispatch_set_target_queue 函数有两个参数 queue 和 targetQueue,dispatch_set_target_queue 有两个功能: queue 和 targetQueue 的优先级, targetQueue 的优先级更高; targetQueue 可以成为 queue 中所有任务的参照队列, 也即 queue 中的任务将依照 targetQueue 的类型特点来执行.
dispatch_set_target_queue(queue, targetQueue);
将不同队列中的任务同步的执行:
- - (void) dispatchSet2 {
- dispatch_queue_t targetQueue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
- dispatch_queue_t queue1 = dispatch_queue_create("QiShareQueue_1", DISPATCH_QUEUE_SERIAL);
- dispatch_queue_t queue2 = dispatch_queue_create("QiShareQueue_2", DISPATCH_QUEUE_CONCURRENT);
- dispatch_set_target_queue(queue1, targetQueue);
- dispatch_set_target_queue(queue2, targetQueue);
- dispatch_async(queue1, ^{
- [NSThread sleepForTimeInterval:5];
- NSLog(@"Task_1");
- });
- dispatch_async(queue2, ^{
- [NSThread sleepForTimeInterval:3];
- NSLog(@"Task_2");
- });
- dispatch_async(queue2, ^{
- [NSThread sleepForTimeInterval:1];
- NSLog(@"Task_3");
- });
- }
- //// 打印日志
- //2019-01-14 17:45:29.920351+0800 QiMultiThread[447:42950] currentThread: <NSThread: 0x1c407c600>{number = 1, name = main}
- //2019-01-14 17:45:29.920424+0800 QiMultiThread[447:42950] ---start---
- //2019-01-14 17:45:29.920474+0800 QiMultiThread[447:42950] ---end---
- //2019-01-14 17:45:34.925556+0800 QiMultiThread[447:42987] Task_1
- //2019-01-14 17:45:37.930813+0800 QiMultiThread[447:42987] Task_2
- //2019-01-14 17:45:38.936053+0800 QiMultiThread[447:42987] Task_3
上述代码中, 我们将 targetQueue 设置为串行, 则 queue1 与 queue2 均参照 targetQueue 的类型特点来执行.
ps:
异步执行 (async) 虽然具有开启新线程的能力, 但是并不一定会开启新线程, 是否开启新线程跟任务所在队列类型有关;
在 dispatch_barrier_(a)sync 方法中, 我们要注意的是不要使用全局并发队列, DISPATCH_QUEUE_CONCURRENT 类型的自定义队列更合适一些.
参考文章链接 https://www.jianshu.com/p/c582d705f68e , 感谢!
工程源码 GitHub 地址 https://github.com/QiShare/QiMultiThread
小编微信: 可加并拉入《QiShare 技术交流群》.
关注我们的途径有:
- QiShare(简书) https://www.jianshu.com/u/3db23baa08c7
- QiShare(掘金)
- QiShare(知乎) https://www.zhihu.com/people/edit
- QiShare(GitHub) https://github.com/QiShare
- QiShare(CocoaChina) http://www.cocoachina.com/bbs/u.php?tid=658244
- QiShare(Stack Overflow) https://stackoverflow.com/users/10118400/qishare
- QiShare(微信公众号)
来源: https://juejin.im/post/5c3c63c5f265da615705b25f