iOS多线程之GCD

2020-04-22  本文已影响0人  Arthurcsh
线程

GCD是苹果公司为多核的并行运算解决方案,会自动利用更多的CPU内核,GCD自动管理线程的生命周期(创建、调度、销毁)

GCD的两个核心概念

同步执行(sync):

  1. 同步添加任务到指定队列中,中任务执行结束之前,会一直等待,直到队列里的任务完成后再执行。
  2. 只能在当前线程中执行任务,不具备开启新线程能力。

异步执行(async):

  1. 异步添加任务到指定的队列中,不会做任务等待,可以继续执行任务。
  2. 可以在新的线程中执行任务,具备开启新线程能力。

串行队列(Serial):


串行队列

每次只有一个任务被执行,只开启一个线程,一个任务执行完毕后,再执行下一个任务。

并发队列(Concurrent):


并发队列
  1. 可以让多个任务并发执行,可以开启多个线程,并且同时执行多任务。
  2. 注意:并发队列的并发功能只有在异步dispatch_async方法下才有效。
    主队列与全局队列:
  3. 主队列中的任务都在主线程中执行,主队列的特点:如果主队列发现当前主线程有任务在执行,那么主队列会暂停调用队列中的任务,直到主线程空闲为止。
  4. 全局队列本质上就是并发队列,与并发队列区别:并发队列有自定义名称,可以跟踪错误,全局队列没有,在ARC中不需要考虑内存释放,在MRC中需要手动dispatch_release(queue)释放内存,全局队列只有一个由系统管理。
  5. 一般情况下使用全局队列,但是对于同一类业务创建一个固定的队列进行管理,如SDK为两使被导入的SDK不影响到主应用的其他业务队列操作,建议创建自己专属的队列。
GCD的使用步骤
  1. 创建一个队列(串行队列或并发队列);
  2. 将任务追加到指定的队列中,系统会根据任务类型执行任务(同步执行或异步执行)。
    使用dispatch_queue_create方法创建队列,两个参数:
// 串行队列,异步执行:会新建一个线程,按串行顺序执行. DISPATCH_QUEUE_SERIAL = NULL
    dispatch_queue_t queue = dispatch_queue_create("com.chshua.dispatch", DISPATCH_QUEUE_SERIAL);
    for (int i=0; i<10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ --- %d", [NSThread currentThread], i);
        });
    }
// 并行队列,异步执行:会新建多个线程,无法确定任务执行顺序
    dispatch_queue_t concurrent = dispatch_queue_create("com.chshua.dispatch", DISPATCH_QUEUE_CONCURRENT);
    for (int i=0; i<10; i++) {
        dispatch_async(concurrent, ^{
            NSLog(@"%@ --- %d", [NSThread currentThread], i);
        });
    }

『主线程』中,『不同队列』+『不同任务』简单组合的区别:

区别 并发队列 串行队列 主队列
同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 死锁卡住不执行
异步(async) 有开启新线程,并发执行任务 有开启新线程(1条),串行执行任务 没有开启新线程, 串行执行任务

注意: 『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。
这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。
而如果我们在 『其他线程』 调用 『主队列』+『同步执行』,则不会阻塞 『主队列』,自然也不会造成死锁问题。最终的结果是:不会开启新线程,串行执行任务。

主队列

  1. 不开线程,同步执行
  2. 主队列特点:如果主线程正在执行代码暂时不调度任务,等主线程执行结束后执行任务
  3. 主队列又叫全局串行队列
  1. 程序执行不出来(死锁)
  2. 死锁原因:主队列:如果主线程正在执行代码,就不调度任务。
    同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完成,再执行下一个任务此时互相等待,程序无法往下执行(死锁)
//主队列的特点:主队列只有当主线程空闲下来的时候才能够执行主队列里面的任务
dispatch_sync(dispatch_get_main_queue(), ^{
       NSLog(@"哈哈, 大家都在等吧...");
});

一次执行dispatch_once
作用是保证block在程序生命周期范围内只执行一次,最常见的场景是单例

+ (instancetype)sharedManager {
    static id instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

延迟执行dispatch_after

/**
* dispatch_time 延迟时间
* dispatch_get_main_queue 延迟执行的队列
**/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_queue_t queue, ^{
        // well to be executed after a specified delay
 });

多次执行dispatch_apply

/**
* iterations  执行的次数  
* queue       提交到的队列  
* block       执行的任务
**/
dispatch_apply(size_t iterations, dispatch_queue_t queue,  ^{
      // code will to be executed
});

阻塞dispatch_barrier

/**
 * 栅栏方法 dispatch_barrier_async
 */
- (void)barrier {
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任务 1
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    });
    dispatch_async(queue, ^{
        // 追加任务 2
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
    });
    
    dispatch_barrier_async(queue, ^{
        // 追加任务 barrier
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
    });
    
    dispatch_async(queue, ^{
        // 追加任务 3
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
    });
    dispatch_async(queue, ^{
        // 追加任务 4
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程
    });
}

队列组dispatch_group

/**
 * 队列组 dispatch_group_notify
 */
- (void)groupNotify {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务 1
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务 2
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务 1、任务 2 都执行完毕后,回到主线程执行下边任务
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        NSLog(@"group---end");
    });
}
/**
 * 队列组 dispatch_group_wait
 */
- (void)groupWait {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务 1
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务 2
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
    });
    
    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"group---end");
}

\color{red}{dispatch\_group\_wait} 相关代码运行输出结果可以看出:
当所有任务执行完成之后,才执行 \color{red}{dispatch\_group\_wait} 之后的操作。但是,使用\color{red}{dispatch\_group\_wait} 会阻塞当前线程。

上一篇 下一篇

猜你喜欢

热点阅读