美文网首页
iOS 多线程之 GCD

iOS 多线程之 GCD

作者: 大成小栈 | 来源:发表于2019-01-11 18:20 被阅读5次

    1.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也是苹果最为推荐开发者使用的多线程框架。

    2. 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) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 主线程调用:死锁导致程序卡死
    其他线程调用:没有开启新线程,串行执行任务

    我们可以运行下面的代码来验证上列表组合中每一项的正确性,如下:

    - (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
    

    3. 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)dispatchGroupNotify {
        
        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]);
        });
        
        dispatch_group_notify(group, queue, ^{
            NSLog(@"dispatch_group_Notify block end");
        });
        
        NSLog(@"---end---");
    }
    
    //// 打印日志
    //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:

    1. 异步执行(async)虽然具有开启新线程的能力,但是并不一定会开启新线程,是否开启新线程跟任务所在队列类型有关;
    2. 在dispatch_barrier_(a)sync方法中,我们要注意的是不要使用全局并发队列,DISPATCH_QUEUE_CONCURRENT类型的自定义队列更合适一些。

    参考文章链接https://www.jianshu.com/p/c582d705f68e,感谢!

    工程源码GitHub地址

    相关文章

      网友评论

          本文标题:iOS 多线程之 GCD

          本文链接:https://www.haomeiwen.com/subject/naikrqtx.html