iOS多线程-GCD

作者: Lxin_ | 来源:发表于2019-04-22 02:06 被阅读19次

GCD,全称Grand Central Dispatch,可译为“牛逼的中枢调度器”, Apple 开发的一个多核编程的解决方法,纯C语言,提供了非常多强大的函数,在 iOS 4 及以上版本使用。

GCD优势

  • GCD是苹果公司为多核的并行运算提出的解决方案。
  • GCD 会自动利用更多的 CPU 内核(比如双核、四核)。
  • GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。
  • 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。

任务与队列

任务

就是要执行的操作,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

同步执行(sync):

  • 添加任务到指定的队列中,在队列中之前的任务执行完之前,会一直等待,直到队列中之前的任务执行完再继续执行。
  • 只能在当前线程中执行任务,不具备开启新线程的能力。

异步执行(async):

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

注意事项:
异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关(下面会讲)。

队列

这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue):

  • 每次只有一个任务被执行。让任务一个接着一个地执行。

并发队列(Concurrent Dispatch Queue):

  • 可以让多个任务并发(同时)执行。

GCD提供了两个特殊队列,一个是串行队列:主队列(Main Dispatch Queue),另一个是并发队列:全局并发队列(Global Dispatch Queue)

队列与任务的创建方法

  • 队列的创建方法
    /**
     串行队列的创建方法
     第一个参数const char *label : C语言字符串,用来标识
     第二个参数dispatch_queue_attr_t attr : 队列的类型
     并发队列:DISPATCH_QUEUE_CONCURRENT
     串行队列:DISPATCH_QUEUE_SERIAL 或者 NULL
     */
    dispatch_queue_t serialQueue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    // 主队列的获取方法
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    /**
     全局并发队列的获取方法
     第一个参数:优先级 也可直接填后面的数字
     #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认
     #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
     第二个参数: 预留参数,暂时没用,用0即可
     */
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 任务的创建方法
    // 同步任务创建方法
    dispatch_sync(queue, ^{
        // 这里放同步任务代码
    });
    // 异步任务创建方法
    dispatch_async(queue, ^{
        // 这里放异步任务代码
    });

任务与队列组合

任务与队列的组合为:

  • 同步任务 + 串行队列
  • 同步任务 + 并发队列
  • 同步任务 + 主队列
  • 异步任务 + 串行队列
  • 异步任务 + 并发队列
  • 异步任务 + 主队列
  • 同步任务 + 全局并发队列
  • 异步任务 + 全局并发队列

基于 全局并发队列 与 普通并发队列 在与任务组合过程中,并无差别,在下文代码展示中,不考虑全局并发队列的组合。

组合原则:任务>队列(主队列除外)

区别 串行队列 并发队列 主队列
同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 死锁卡住不执行
异步(async) 有开启新线程(1条),串行执行任务 有开启新线程,并发执行任务 没有开启新线程,串行执行任务
  1. 同步任务 + 串行队列
/// 同步任务 + 串行队列
- (void)syncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"syncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    NSLog(@"syncSerial---end");
    /* 结果 - 没有开启新线程,串行执行任务
     2019-04-22 01:24:07.414305+0800 GCD[1843:3137709] currentThread---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:07.414408+0800 GCD[1843:3137709] syncSerial---begin
     2019-04-22 01:24:09.414778+0800 GCD[1843:3137709] 1---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:11.416190+0800 GCD[1843:3137709] 1---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:13.416811+0800 GCD[1843:3137709] 2---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:15.418364+0800 GCD[1843:3137709] 2---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:17.419142+0800 GCD[1843:3137709] 3---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:19.420796+0800 GCD[1843:3137709] 3---<NSThread: 0x600000a6d380>{number = 1, name = main}
     2019-04-22 01:24:19.421075+0800 GCD[1843:3137709] syncSerial---end
     */
}
  1. 同步任务 + 并发队列
/// 同步任务 + 并发队列
- (void)syncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");

    dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    NSLog(@"syncConcurrent---end");
    /* 结果 - 没有开启新线程,串行执行任务
     2019-04-22 01:28:24.544927+0800 GCD[1905:3172908] currentThread---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:24.545157+0800 GCD[1905:3172908] syncConcurrent---begin
     2019-04-22 01:28:26.545693+0800 GCD[1905:3172908] 1---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:28.547100+0800 GCD[1905:3172908] 1---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:30.548452+0800 GCD[1905:3172908] 2---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:32.549368+0800 GCD[1905:3172908] 2---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:34.550001+0800 GCD[1905:3172908] 3---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:36.550777+0800 GCD[1905:3172908] 3---<NSThread: 0x60000136e980>{number = 1, name = main}
     2019-04-22 01:28:36.551122+0800 GCD[1905:3172908] syncConcurrent---end
     */
}
  1. 同步任务 + 主队列
/// 同步任务 + 主队列
- (void)syncMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncMain---begin");

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"syncMain---end");
    /* 结果 - crash在 dispatch_sync(queue, ^{ 这一行
     2019-04-22 01:35:18.443455+0800 GCD[1970:3228432] currentThread---<NSThread: 0x600003f39fc0>{number = 1, name = main}
     2019-04-22 01:35:18.443574+0800 GCD[1970:3228432] syncMain---begin
     (lldb)
     */
}
  1. 异步任务 + 串行队列
/// 异步任务 + 串行队列
- (void)asyncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"asyncSerial---end");
    /* 结果 - 有开启新线程(1条),串行执行任务
     2019-04-22 01:38:08.254765+0800 GCD[2008:3251926] currentThread---<NSThread: 0x6000019cbd00>{number = 1, name = main}
     2019-04-22 01:38:08.254873+0800 GCD[2008:3251926] asyncSerial---begin
     2019-04-22 01:38:08.254960+0800 GCD[2008:3251926] asyncSerial---end
     2019-04-22 01:38:10.255499+0800 GCD[2008:3252126] 1---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     2019-04-22 01:38:12.259319+0800 GCD[2008:3252126] 1---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     2019-04-22 01:38:14.261561+0800 GCD[2008:3252126] 2---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     2019-04-22 01:38:16.265208+0800 GCD[2008:3252126] 2---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     2019-04-22 01:38:18.265802+0800 GCD[2008:3252126] 3---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     2019-04-22 01:38:20.266188+0800 GCD[2008:3252126] 3---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
     */
}
  1. 异步任务 + 并发队列
/// 异步任务 + 并发队列
- (void)asyncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncConcurrent---begin");

    dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"asyncConcurrent---end");
    /* 结果 - 有开启新线程,并发执行任务
     2019-04-22 01:40:21.313343+0800 GCD[2042:3270436] currentThread---<NSThread: 0x600001ac8040>{number = 1, name = main}
     2019-04-22 01:40:21.313457+0800 GCD[2042:3270436] asyncConcurrent---begin
     2019-04-22 01:40:21.313545+0800 GCD[2042:3270436] asyncConcurrent---end
     2019-04-22 01:40:23.318284+0800 GCD[2042:3270652] 3---<NSThread: 0x600001a9f780>{number = 4, name = (null)}
     2019-04-22 01:40:23.318318+0800 GCD[2042:3270655] 1---<NSThread: 0x600001a9b680>{number = 3, name = (null)}
     2019-04-22 01:40:23.318401+0800 GCD[2042:3270656] 2---<NSThread: 0x600001a8a480>{number = 5, name = (null)}
     2019-04-22 01:40:25.319365+0800 GCD[2042:3270652] 3---<NSThread: 0x600001a9f780>{number = 4, name = (null)}
     2019-04-22 01:40:25.319386+0800 GCD[2042:3270655] 1---<NSThread: 0x600001a9b680>{number = 3, name = (null)}
     2019-04-22 01:40:25.319441+0800 GCD[2042:3270656] 2---<NSThread: 0x600001a8a480>{number = 5, name = (null)}
     */
}
  1. 异步任务 + 主队列
/// 异步任务 + 主队列
- (void)asyncMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---begin");

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"asyncMain---end");
    /* 结果 - 没有开启新线程,串行执行任务
     2019-04-22 01:42:23.007175+0800 GCD[2075:3287465] currentThread---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:23.007279+0800 GCD[2075:3287465] asyncMain---begin
     2019-04-22 01:42:23.007345+0800 GCD[2075:3287465] asyncMain---end
     2019-04-22 01:42:25.011764+0800 GCD[2075:3287465] 1---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:27.013156+0800 GCD[2075:3287465] 1---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:29.013723+0800 GCD[2075:3287465] 2---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:31.015146+0800 GCD[2075:3287465] 2---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:33.016033+0800 GCD[2075:3287465] 3---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     2019-04-22 01:42:35.017703+0800 GCD[2075:3287465] 3---<NSThread: 0x6000013f8d00>{number = 1, name = main}
     */
}

总结

  1. 任务>队列(主队列除外)。
  2. 开不开线程,取决于任务同步任务不开新线程,异步任务开新线程。
  3. 开几条线程取决于队列串行队列开一条线程,并行队列在执行多个异步任务时会开辟多条线程。
  4. 同步任务+主队列会crash,异步任务+主队列不会开线程,在主线程执行任务

相关文章

  • iOS多线程:『GCD』详尽总结

    iOS多线程:『GCD』详尽总结 iOS多线程:『GCD』详尽总结

  • iOS多线程(一)-GCD

    iOS多线程-GCD

  • iOS多线程相关面试题

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • 多线程之--NSOperation

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • iOS多线程之--NSThread

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • [iOS 多线程] iOS多线程-GCD

    iOS多线程-GCD GCD的简介 GCD,全称为 Grand Central Dispatch ,是iOS用来管...

  • iOS 多线程

    iOS 多线程有几种方式 GCD NSOpeartion NSThread phread 多线程 GCD disp...

  • GCD练习

    GCD练习 ios 多线程 GCD : ios 多线程 全剧队列,异步执行 线程间通信 信号量 文件锁 单利模式 ...

  • iOS开发多线程之GCD

    iOS开发多线程之GCDiOS开发之GCD同步任务加强iOS开发之GCD串行队列iOS开发之GCD并发队列 GCD...

  • iOS 多线程

    参考链接 iOS多线程iOS 多线程:『GCD』详尽总结iOS简单优雅的实现复杂情况下的串行需求(各种锁、GCD ...

网友评论

    本文标题:iOS多线程-GCD

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