iOS 多线程之GCD

栏目: IOS · 发布时间: 5年前

内容简介:Grand Central Dispatch(GCD)是由苹果开发的多线程调度框架,能够优化多线程应用程序的执行过程以支持多核处理器。GCD底层有线程池,线程池的概念在上一篇文章中有简单介绍,线程池是系统自动来维护,开发者只关注队列(Dispatch Queue)及任务的创建和同步异步调用即可。 iOS中使用GCD来实现线程操作很简单:创建队列 > 同步/异步调用 > 将任务放在上一步调用的block中。下面我们来实现异步执行耗时操作,当耗时操作执行完毕时,回到主线程来更新相应的UI的功能,简易的代码如下:

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

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

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

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

参考文章链接,感谢!

工程源码GitHub地址

小编微信:可加并拉入《QiShare技术交流群》。

iOS 多线程之GCD

关注我们的途径有:

QiShare(简书)

QiShare(掘金)

QiShare(知乎)

QiShare(GitHub)

QiShare(CocoaChina)

QiShare(StackOverflow)

QiShare(微信公众号)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithm Analysis in Java

Data Structures and Algorithm Analysis in Java

Mark A. Weiss / Pearson / 2011-11-18 / GBP 129.99

Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course wa......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换