iOS多线程-GCD 附带GCD相关代码

多线程学习笔记-GCD


GCD、NSThread、NSOperation区别

1. NSThread
每个NSThread对象对应一个线程,真正最原始的线程。
1)优点:NSThread 轻量级最低,相对简单。
2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。
2. NSOperation
自带线程管理的抽象类。
1)优点:自带线程周期管理,操作上可更注重自己逻辑。
2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
3. GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。
1)优点:最高效,避开并发陷阱。
2)缺点:基于C实现。
4. 选择小结
1)简单而安全的选择NSOperation实现多线程即可。
2)处理大量并发数据,又追求性能效率的选择GCD。
3)NSThread本人选择基本上是在做些小测试上使用,当然也可以基于此造个轮子。


1.GCD的好处

  • GCD可用于多核的并行运算
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

2.线程、进程、任务与队列

  • 任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行和异步执行。两者的主要区别是:是否具备开启新线程的能力。
  • 同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
  • 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行,不具备开启新线程的能力。
  • 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程,具备开启新线程的能力。
  • 队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并发队列 (其实还有一种特殊的队列:主队列),后面再详细说。
  • 并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    并发功能只有在异步(dispatch_async)函数下才有效
  • 串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
  • 并发VS并行:并行是基于多核设备的,并行一定是并发,并发不一定是并行。
  • 线程与进程
  • 进程概念: 进程是程序在计算机上的一次执行活动,打开一个app,就开启了一个进程,可包含多个线程。
  • 线程概念: 独立执行的代码段,一个线程同时间只能执行一个任务,反之多线程并发就可以在同一时间执行多个任务。
  • 同步线程:同步线程会阻塞当前线程去执行线程内的任务,执行完之后才会反回当前线程。
  • 异步线程:异步线程不会阻塞当前线程,会开启其他线程去执行线程内的任务。
  • 主线程(又叫作UI线程)主要任务是处理UI事件,显示和刷新UI,(只有主线程有直接修改UI的能力)耗时的操作放在子线程(又叫作后台线程、异步线程)。

GCD总结代码下载地址-GCD-wxk

3.GCD使用

  • 创建一个队列(串行队列或并发队列)
  • 将任务添加到队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)
    注意:Dispatch Queue队列并不是指我们印象中的线程,它是任务队列,它只负责任务的管理调度,并不进行任务的执行操作,任务的执行是由Dispatch Queue分配的线程来完成的
  • 创建队列
// 创建队列
    // 串行队列的创建方法
    dispatch_queue_t queue_1 = dispatch_queue_create("wxk_queue_1", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t queue_2 = dispatch_queue_create("wxk_queue_2", DISPATCH_QUEUE_CONCURRENT);
    // 创建全局并发队列
    dispatch_queue_t queue_3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。
使用dispatch_get_global_queue来创建全局并发队列。GCD默认提供了全局的并发队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。

  • 创建任务
 // 同步执行任务
    dispatch_sync(queue_1, ^{
       // 这里写任务代码
    });
    
    // 异步执行任务
    dispatch_async(queue_1, ^{
       // 这里写任务代码
    });
  • 不同队列与不同任务执行方式的组合
    • 并发队列 + 同步执行
    • 并发队列 + 异步执行
    • 串行队列 + 同步执行
    • 串行队列 + 异步执行
    • 主队列 + 同步执行
    • 主队列 + 异步执行

4.GCD使用实例

4.1 并发队列 + 同步执行

  • 不会开启新线程,执行完一个任务,再执行下一个任务
#pragma mark - 并发队列-同步执行
- (void) concurrent_sync
{
    NSLog(@"并发队列-同步执行 -- 开始");
    // 并发队列的创建方法
    dispatch_queue_t queue_2 = dispatch_queue_create("wxk_queue_2", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    NSLog(@"并发队列-同步执行 -- 结束");
}
// 输出
2017-07-03 16:58:59.331 GCD[1319:535532] 并发队列-同步执行 -- 开始
2017-07-03 16:58:59.331 GCD[1319:535532] 线程1--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.332 GCD[1319:535532] 线程1--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.332 GCD[1319:535532] 线程2--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.332 GCD[1319:535532] 线程2--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.332 GCD[1319:535532] 线程3--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.333 GCD[1319:535532] 线程3--<NSThread: 0x60800006f880>{number = 1, name = main}
2017-07-03 16:58:59.333 GCD[1319:535532] 并发队列-同步执行 -- 结束
  • 并发队列 + 同步执行中可以看到,所有任务都是在主线程中执行的。由于只有一个线程,所以任务只能一个一个执行。
  • 同时我们还可以看到,所有任务都在打印的‘’开始‘’‘’结束‘’之间,这说明任务是添加到队列中马上执行的。

4.2并发队列 + 异步执行

  • 可同时开启多线程,任务交替执行
#pragma mark - 并发队列-异步执行
- (void) concurrent_async
{
    NSLog(@"并发队列-异步执行 -- 开始");
    // 并发队列的创建方法
    dispatch_queue_t queue_2 = dispatch_queue_create("wxk_queue_2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    NSLog(@"并发队列-异步执行 -- 结束");
}
// 输出
2017-07-03 17:16:45.376 GCD[1351:567851] 并发队列-异步执行 -- 开始
2017-07-03 17:16:45.376 GCD[1351:567851] 并发队列-异步执行 -- 结束
2017-07-03 17:16:45.376 GCD[1351:568290] 线程2--<NSThread: 0x6000002659c0>{number = 4, name = (null)}
2017-07-03 17:16:45.376 GCD[1351:568189] 线程1--<NSThread: 0x608000262580>{number = 3, name = (null)}
2017-07-03 17:16:45.376 GCD[1351:568190] 线程3--<NSThread: 0x600000265800>{number = 5, name = (null)}
2017-07-03 17:16:45.377 GCD[1351:568290] 线程2--<NSThread: 0x6000002659c0>{number = 4, name = (null)}
2017-07-03 17:16:45.377 GCD[1351:568189] 线程1--<NSThread: 0x608000262580>{number = 3, name = (null)}
2017-07-03 17:16:45.377 GCD[1351:568190] 线程3--<NSThread: 0x600000265800>{number = 5, name = (null)}
  • 并发队列 + 异步执行中可以看出,除了主线程,又开启了3个线程,并且任务是交替着同时执行的。
  • 所有任务是在打印的‘’开始‘’‘’结束‘’之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始异步执行。

4.3串行队列 + 同步执行

  • 不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
#pragma mark - 串行队列-同步执行
- (void) serial_sync
{
    NSLog(@"并发队列-异步执行 -- 开始");
    // 串行队列的创建方法
    dispatch_queue_t queue_1 = dispatch_queue_create("wxk_queue_1", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    NSLog(@"并发队列-异步执行 -- 结束");
}
// 输出
2017-07-03 17:24:09.906 GCD[1385:605920] 并发队列-异步执行 -- 开始
2017-07-03 17:24:09.907 GCD[1385:605920] 线程1--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.907 GCD[1385:605920] 线程1--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.907 GCD[1385:605920] 线程2--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.908 GCD[1385:605920] 线程2--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.908 GCD[1385:605920] 线程3--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.908 GCD[1385:605920] 线程3--<NSThread: 0x6000000673c0>{number = 1, name = main}
2017-07-03 17:24:09.908 GCD[1385:605920] 并发队列-异步执行 -- 结束
  • 串行队列 + 同步执行可以看到,所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行。
  • 同时我们还可以看到,所有任务都在打印的‘’开始‘’‘’结束‘’之间,这说明任务是添加到队列中马上执行的。

4.4串行队列 + 异步执行

  • 会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务
#pragma mark - 串行队列-异步执行
- (void) serial_async
{
    NSLog(@"并发队列-异步执行 -- 开始");
    // 串行队列的创建方法
    dispatch_queue_t queue_1 = dispatch_queue_create("wxk_queue_1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_1, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"并发队列-异步执行 -- 结束");
}
// 输出
2017-07-03 17:27:24.452 GCD[1405:625719] 并发队列-异步执行 -- 开始
2017-07-03 17:27:24.452 GCD[1405:625719] 并发队列-异步执行 -- 结束
2017-07-03 17:27:24.452 GCD[1405:626097] 线程1--<NSThread: 0x600000078a00>{number = 3, name = (null)}
2017-07-03 17:27:24.453 GCD[1405:626097] 线程1--<NSThread: 0x600000078a00>{number = 3, name = (null)}
2017-07-03 17:27:24.453 GCD[1405:626097] 线程2--<NSThread: 0x600000078a00>{number = 3, name = (null)}
2017-07-03 17:27:24.453 GCD[1405:626097] 线程2--<NSThread: 0x600000078a00>{number = 3, name = (null)}
2017-07-03 17:27:24.453 GCD[1405:626097] 线程3--<NSThread: 0x600000078a00>{number = 3, name = (null)}
2017-07-03 17:27:24.454 GCD[1405:626097] 线程3--<NSThread: 0x600000078a00>{number = 3, name = (null)}
  • 串行队列 + 异步执行可以看到,开启了一条新线程,但是任务还是串行,所以任务是一个一个执行。
  • 另一方面可以看出,所有任务是在打印的‘’开始‘’‘’结束‘’之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

5 主队列

  • 主队列:GCD自带的一种特殊的串行队列
  • 所有放在主队列中的任务,都会放到主线程中执行
  • 可使用dispatch_get_main_queue()获得主队列

5.1 主队列 + 同步执行

  • 互等卡住不可行(在主线程中调用)
#pragma mark - 主队列-同步执行
- (void) main_sync
{
    NSLog(@"并发队列-异步执行 -- 开始");
    // 获取主队列
    dispatch_queue_t queue_main = dispatch_get_main_queue();
    dispatch_sync(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"并发队列-异步执行 -- 结束");
}
// 输出
2017-07-03 17:34:36.768 GCD[1457:663325] 并发队列-异步执行 -- 开始

卡住了,这是因为我们在主线程中执行这段代码。我们把任务放到了主队列中,也就是放到了主线程的队列中。而同步执行有个特点,就是对于任务是立马执行的。那么当我们把第一个任务放进主队列中,它就会立马执行。但是主线程现在正在处理main_sync方法,所以任务需要等main_sync执行完才能执行。而main_sync执行到第一个任务的时候,又要等第一个任务执行完才能往下执行第二个和第三个任务。

那么,现在的情况就是main_sync方法和第一个任务都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且‘结束’也没有打印。

  • main_sync放到另一个线程去执行
#pragma mark - 修改主队列main_sync方法卡住问题
- (void) advance_main_sync
{
    dispatch_queue_t queue_2 = dispatch_queue_create("wxk_queue_2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue_2, ^{
        [self main_sync];
    });
}
// 输出
2017-07-03 17:53:09.550 GCD[1541:728425] 并发队列-异步执行 -- 开始
2017-07-03 17:53:09.557 GCD[1541:728236] 线程1--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.557 GCD[1541:728236] 线程1--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.559 GCD[1541:728236] 线程2--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.561 GCD[1541:728236] 线程2--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.562 GCD[1541:728236] 线程3--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.563 GCD[1541:728236] 线程3--<NSThread: 0x60800007fd40>{number = 1, name = main}
2017-07-03 17:53:09.563 GCD[1541:728425] 并发队列-异步执行 -- 结束
  • 在其他线程中使用主队列 + 同步执行可看到:所有任务都是在主线程中执行的,并没有开启新的线程。而且由于主队列是串行队列,所以按顺序一个一个执行。
  • 同时我们还可以看到,所有任务都在打印的‘’开始‘’‘’结束‘’之间,这说明任务是添加到队列中马上执行的。

5.2 主队列 + 异步执行

  • 只在主线程中执行任务,执行完一个任务,再执行下一个任务
#pragma mark - 主队列+异步任务
- (void) main_async
{
    NSLog(@"并发队列-异步执行 -- 开始");
    // 获取主队列
    dispatch_queue_t queue_main = dispatch_get_main_queue();
    dispatch_async(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_main, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"并发队列-异步执行 -- 结束");
}
// 输出
2017-07-03 17:55:42.924 GCD[1561:745372] 并发队列-异步执行 -- 开始
2017-07-03 17:55:42.925 GCD[1561:745372] 并发队列-异步执行 -- 结束
2017-07-03 17:55:42.931 GCD[1561:745372] 线程1--<NSThread: 0x608000078400>{number = 1, name = main}
2017-07-03 17:55:42.931 GCD[1561:745372] 线程1--<NSThread: 0x608000078400>{number = 1, name = main}
2017-07-03 17:55:42.932 GCD[1561:745372] 线程2--<NSThread: 0x608000078400>{number = 1, name = main}
2017-07-03 17:55:42.932 GCD[1561:745372] 线程2--<NSThread: 0x608000078400>{number = 1, name = main}
2017-07-03 17:55:42.932 GCD[1561:745372] 线程3--<NSThread: 0x608000078400>{number = 1, name = main}
2017-07-03 17:55:42.933 GCD[1561:745372] 线程3--<NSThread: 0x608000078400>{number = 1, name = main}
  • 我们发现所有任务都在主线程中,虽然是异步执行,具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中,并且一个接一个执行。
  • 另一方面可以看出,所有任务是在打印的‘’开始‘’‘’结束‘’之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

6.GCD的其他用法

6.1线程之间的通讯

  • 图片加载的时候我们把加载图片的耗时操作放在其他线程里,加载完成后再到主线程刷新UI的操作。
#pragma mark - 线程之间的通讯
- (void) exchange
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 这里执行耗时操作
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"耗时操作--%@",[NSThread currentThread]);
        }
        // 回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            // 做完耗时操作后,在主线程刷新UI
            NSLog(@"刷新UI--%@",[NSThread currentThread]);
        });
        
    });
}
// 输出
2017-07-04 14:33:56.055 GCD[951:186144] 耗时操作--<NSThread: 0x608000072b00>{number = 3, name = (null)}
2017-07-04 14:33:56.056 GCD[951:186144] 耗时操作--<NSThread: 0x608000072b00>{number = 3, name = (null)}
2017-07-04 14:33:56.062 GCD[951:185945] 刷新UI--<NSThread: 0x600000068ec0>{number = 1, name = main}
  • 可以看到在其他线程中先执行操作,执行完了之后回到主线程执行主线程的相应操作。

6.2 GCD栅栏方法dispatch_barrier_async

  • 我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。
#pragma mark - 栅栏方法
- (void) barrier
{
    dispatch_queue_t queue_2 = dispatch_queue_create("wxk_queue_2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程2--%@",[NSThread currentThread]);
        }
    });
    dispatch_barrier_async(queue_2, ^{
        NSLog(@"栅栏方法--%@", [NSThread currentThread]);
    });
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程3--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue_2, ^{
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"线程4--%@",[NSThread currentThread]);
        }
    });
}
// 输出
2017-07-04 15:24:53.985 GCD[1007:250229] 线程2--<NSThread: 0x600000268240>{number = 4, name = (null)}
2017-07-04 15:24:53.985 GCD[1007:250226] 线程1--<NSThread: 0x6000002683c0>{number = 3, name = (null)}
2017-07-04 15:24:53.985 GCD[1007:250226] 线程1--<NSThread: 0x6000002683c0>{number = 3, name = (null)}
2017-07-04 15:24:53.985 GCD[1007:250229] 线程2--<NSThread: 0x600000268240>{number = 4, name = (null)}
2017-07-04 15:24:53.985 GCD[1007:250229] 栅栏方法--<NSThread: 0x600000268240>{number = 4, name = (null)}
2017-07-04 15:24:53.986 GCD[1007:250229] 线程3--<NSThread: 0x600000268240>{number = 4, name = (null)}
2017-07-04 15:24:53.986 GCD[1007:250226] 线程4--<NSThread: 0x6000002683c0>{number = 3, name = (null)}
2017-07-04 15:24:53.986 GCD[1007:250229] 线程3--<NSThread: 0x600000268240>{number = 4, name = (null)}
2017-07-04 15:24:53.986 GCD[1007:250226] 线程4--<NSThread: 0x6000002683c0>{number = 3, name = (null)}
  • 可以看出在执行完栅栏前面的操作之后,才执行栅栏操作,最后再执行栅栏后边的操作。

6.3 GCD的延时执行方法 dispatch_after

#pragma mark - 延时执行方法
- (void) dispatch_after
{
    NSLog(@"开始开始");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"3秒后执行这段代码");
    });
}
// 输出
2017-07-04 15:33:27.837 GCD[1048:271319] 开始开始
2017-07-04 15:33:30.838 GCD[1048:271319] 3秒后执行这段代码
  • 可以看出时间差 3秒后执行的代码

6.4 GCD的一次性代码(只执行一次)dispatch_once

  • 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });

6.5 GCD的快速迭代方法 dispatch_apply

  • 通常我们会用for循环遍历,但是GCD给我们提供了快速迭代的方法 dispatch_apply,使我们可以同时遍历。比如说遍历0~5这6个数字,for循环的做法是每次取出一个元素,逐个遍历。dispatch_apply可以同时遍历多个数字。
#pragma mark - 快速迭代方法
- (void) dispatch_apply
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
    });
}
// 输出
2017-07-04 15:47:38.918 GCD[1088:305847] 0------<NSThread: 0x60000007efc0>{number = 1, name = main}
2017-07-04 15:47:38.918 GCD[1088:305902] 1------<NSThread: 0x60000026b780>{number = 3, name = (null)}
2017-07-04 15:47:38.918 GCD[1088:305903] 2------<NSThread: 0x60000026b640>{number = 4, name = (null)}
2017-07-04 15:47:38.918 GCD[1088:305905] 3------<NSThread: 0x60000026c5c0>{number = 5, name = (null)}
2017-07-04 15:47:38.919 GCD[1088:305847] 4------<NSThread: 0x60000007efc0>{number = 1, name = main}
2017-07-04 15:47:38.919 GCD[1088:305902] 5------<NSThread: 0x60000026b780>{number = 3, name = (null)}
  • 从输出结果中前边的时间中可以看出,几乎是同时遍历的。

6.6 GCD的队列组 dispatch_group

  • 有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。
  • 我们可以先把任务放到队列中,然后将队列放入队列组中。
  • 调用队列组的dispatch_group_notify回到主线程执行操作。
#pragma mark - GCD的队列组
- (void) dispatch_group
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 执行1个耗时的异步操作
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"耗时操作1--%@",[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 执行1个耗时的异步操作
        
        for (int i = 0; i < 2; i++)
        {
            NSLog(@"耗时操作2--%@",[NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"等前面的异步操作都执行完毕后,回到主线程...");
    });
}
// 输出
2017-07-04 15:54:10.056 GCD[1132:331040] 耗时操作2--<NSThread: 0x608000265c80>{number = 3, name = (null)}
2017-07-04 15:54:10.056 GCD[1132:331025] 耗时操作1--<NSThread: 0x60800007af80>{number = 4, name = (null)}
2017-07-04 15:54:10.056 GCD[1132:331040] 耗时操作2--<NSThread: 0x608000265c80>{number = 3, name = (null)}
2017-07-04 15:54:10.056 GCD[1132:331025] 耗时操作1--<NSThread: 0x60800007af80>{number = 4, name = (null)}
2017-07-04 15:54:10.062 GCD[1132:330947] 等前面的异步操作都执行完毕后,回到主线程...
  • 可以看到是在前面异步操作都结束的时候,回到主线程做相应操作。

看了一些GCD的文章后总结了下,方便以后慢慢丰富GCD的知识总结代码下载地址-GCD-wxk

GCD.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容