美文网首页
iOS 多线程--GCD

iOS 多线程--GCD

作者: SunZzzl | 来源:发表于2017-05-25 15:32 被阅读24次

    前言:本文主要是关于GCD和NSOperation的具体使用,以及关于线程安全的实现.

    线程以及进程的基本概念就不赘述了.

    GCD

    首先需要明确几个基本的概念:

    • 串行(同步)执行与并发(异步)执行
    • 同步函数与异步函数
    • 串行队列与并发队列
    串行执行与并发执行
     这里的串行执行与并发执行决定的是任务的执行方式
    
    • 串行执行: 如果我没有执行完,那么后面的将永远无法执行
    • 并发执行:我无所谓,我没执行完后面的也可以执行
    同步函数与异步函数
    • 同步函数(disaptach_sync):
      特点:只能在当前线程中执行任务,不具备开启新线程的能力
      执行方式:同步
    • 异步函数(disaptach_async):
      特点:可以在新的线程中执行任务,具备开启新线程的能力
      执行方式:异步
      注意:任务从队列中取出来时时有有序的,遵循FIFO原则,先进先出,但是任务在线程中执行是无序的,并且如果遇到卡顿 阻塞时 可以先继续往下执行,等前面的任务执行完毕之后再回头执行之前遇到卡顿没有执行的任务.
    串行队列与并发队列

    说到队列就得提下GCD中的两个核心概念: 任务与队列
    任务: 执行什么操作 队列: 用来存放任务
    队列分两种就是我们要说的串行队列与并发队列

    • Serial Diapatch Queue 串行队列

    当任务相互依赖,具有明显的先后顺序的时候,使用串行队列是一个不错的选择 创建一个串行队列:
    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);

    第一个参数为队列名,第二个参数为队列类型,当然,第二个参数如果写NULL,创建出来的也是一个串行队列。
    
    通过异步函数来执行
    

    dispatch_async(serialDispatchQueue, ^{
    NSLog(@"1---%@",[NSThread currentThread]);
    });

    dispatch_async(serialDispatchQueue, ^{
        sleep(2);
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    dispatch_async(serialDispatchQueue, ^{
        sleep(1);
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    
    控制台log的结果
    

    2017-05-24 15:57:02.842495 多线程[14286:6618708] 1---<NSThread: 0x170072ec0>{number = 3, name = (null)}
    2017-05-24 15:57:04.847125 多线程[14286:6618708] 2---<NSThread: 0x170072ec0>{number = 3, name = (null)}
    2017-05-24 15:57:05.852685 多线程[14286:6618708] 3----<NSThread: 0x170072ec0>{number = 3, name = (null)}

    可以看到三个任务是在开启的子线程中串行输出,相互彼此依赖,串行执行
    
    - Concurrent Diapatch Queue 并发队列
    与串行队列刚好相反,他不会存在任务间的相互依赖。
    

    创建一个并发队列:
    dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
    比较2个队列的创建,我们发现只有第二个参数从DISPATCH_QUEUE_SERIAL变成了对应的DISPATCH_QUEUE_CONCURRENT,其他完全一样。

    
        用同一段代码,换一种队列我们来比较一下效果:
    
        dispatch_queue_t concurrentDispatchQueue      =          dispatch_queue_create("com.test.queue",    DISPATCH_QUEUE_CONCURRENT);
       dispatch_async(concurrentDispatchQueue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
         });
    
       dispatch_async(concurrentDispatchQueue, ^{
        sleep(2);
        NSLog(@"2---%@",[NSThread currentThread]);
       });
    
        dispatch_async(concurrentDispatchQueue, ^{
        sleep(1);
        NSLog(@"3----%@",[NSThread currentThread]);
        });
    


    2017-05-24 17:10:46.003170 多线程[14299:6629156] 1---<NSThread: 0x170075300>{number = 3, name = (null)}
    2017-05-24 17:10:47.005762 多线程[14299:6629157] 3----<NSThread: 0x174061cc0>{number = 4, name = (null)}
    2017-05-24 17:10:48.007882 多线程[14299:6629156] 2---<NSThread: 0x170075300>{number = 3, name = (null)}

    我们发现,log的输出在3个不同编号的线程中进行,而且相互不依赖,不阻塞。
    
    - Global Queue & Main Queue
    
    这是系统为我们准备的2个队列:
    
    Global Queue 其实就是系统创建的Concurrent Diapatch Queue
    Main Queue   其实就是系统创建的位于主线程的Serial Diapatch Queue
    通常情况我们会把这2个队列放在一起使用,也是我们最常用的开异步线程-执行异步任务-回主线程的一种方式:
    
    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"异步线程");
    dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"异步主线程");
    });
    });

    通过上面的代码我们发现了2个点:
    
    - dispatch_get_global_queue存在优先级,没错,他一共有4个优先级:
    

    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

    
    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    NSLog(@"4");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    NSLog(@"3");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"2");
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"1");
    });

    在指定优先级之后,同一个队列会按照这个优先级执行,打印的顺序为1、2、3、4,当然这不是串行队列,所以不存在绝对回调先后。
    
    异步主线程
    
    在日常工作中,除了在其他线程返回主线程的时候需要用这个方法,还有一些时候我们在主线程中直接调用异步主线程,这是利用dispatch_async的特性:block中的任务会放在主线程本次runloop之后返回。这样,有些存在先后顺序的问题就可以得到解决了。
    
    ####关于线程与队列,以及任务的执行方式总结
    ![046EB302-B057-4D4D-9919-180BA087F1A0.png](https://img.haomeiwen.com/i1921775/40c0d60acf555da7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    *这里需要注意: 同步函数和主队列相结合时:  因为同步函数不会开新的线程,任务是在当前的线程中执行,如果当前线程是主线程,那么会造成死锁,也就是队列里面的任务都执行不了.造成的原因是: 因为主队列中的任务是需要在主线程中执行,而在执行之前会先检查主线程的状态,如果发现主线程正在执行任务,那么就会暂停队列中任务的调度,这里需要明白使用同步函数封装任务时,任务的执行必须得拿到当前函数的返回值,才会继续往下面执行, 因为每一条代码都是一个任务,当主线程执行到当前同步函数时,也需要去执行里面封装的block任务块,而此时任务块正在等主线程执行完任务,所以就没办法被执行,所以当前的同步函数就执行不完,也就拿不到返回值,所以就会执行不下去,造成了死锁.
    相反回到最开始,如果当前线程不是主线程,而是新开的子线程的话就不会造成死锁,原因还是:主队列本身的任务调度时的特点
    所以切记: 主线程不能与主队列结合使用.*
    
    *异步函数与主队列结合时不会造成死锁,原因是: 首先明确当前情况下不会开新的线程,都是在主线程中执行的, 同样主队列中的任务执行前也会判断主线程的状态,当前主线程正在执行异步函数这个任务,所以也会暂停调度主队列中的任务, 但是由于异步函数本身的特点,可以继续往下执行,先把每个异步函数代码都执行完之后,回头再去执行每个异步函数里面封装的任务.*
    
    ####GCD中其它常用的函数
    - 主线程中延迟执行:dispatch_after
    这个是最常用的,用来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用它是最合适的.
    
    用三种队列进行了测试
    
    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"主队列----%@",[NSThread currentThread]);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
        NSLog(@"全局并发队列----%@",[NSThread currentThread]);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), serialDispatchQueue, ^{
        NSLog(@"串行队列----%@",[NSThread currentThread]);
    });
    
    控制台输出结果
    
    

    2017-05-24 18:07:22.709788 09-掌握-GCD的常用函数[14307:6637921] 主队列----<NSThread: 0x17007a6c0>{number = 1, name = main}
    2017-05-24 18:07:22.710247 09-掌握-GCD的常用函数[14307:6638005] 全局并发队列----<NSThread: 0x170267bc0>{number = 3, name = (null)}
    2017-05-24 18:07:22.710652 09-掌握-GCD的常用函数[14307:6638071] 串行队列----<NSThread: 0x17026ba40>{number = 4, name = (null)}

    
    结论:
    

    a.GCD延迟执行的原理是,延迟把block内容提交到队列而非直接添加到队列后延迟调度
    b.GCD可以通过设置队列来控制block在哪个线程中调用
    c.dispatch_after方法本身是异步的(类似dispatch_async) 具备开启线程的能力

    
    - 栅栏函数:dispatch_barrier_async
    直接上代码
    
    
    //创建队列
    dispatch_queue_t queue = dispatch_queue_create("szl", DISPATCH_QUEUE_CONCURRENT);
    
    //2添加任务
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 50; i++) {
            NSLog(@"1-%zd---%@",i,[NSThread currentThread]);
        }
        
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"2-%zd---%@",i,[NSThread currentThread]);
        }
    });
    
    //控制顺序:必须等任务1和2执行完毕之后才能执行任务3和任务4
    dispatch_barrier_async(queue, ^{
        
        NSLog(@"+++++++++++++++++++++++++++");
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i <10; i++) {
            NSLog(@"3-%zd---%@",i,[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i <10; i++) {
            NSLog(@"4-%zd---%@",i,[NSThread currentThread]);
        }
    });
    
    
    控制台输出结果
    

    2017-05-25 12:03:40.173683 多线程[14462:6789088] 1-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.173840 多线程[14462:6789088] 1-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.173877 多线程[14462:6789088] 1-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.173910 多线程[14462:6789088] 1-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.173943 多线程[14462:6789088] 1-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.173976 多线程[14462:6789088] 1-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.174008 多线程[14462:6789088] 1-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.174041 多线程[14462:6789088] 1-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.174073 多线程[14462:6789088] 1-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.174106 多线程[14462:6789088] 1-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.174169 多线程[14462:6789089] 2-0---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.176032 多线程[14462:6789088] 1-10---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177201 多线程[14462:6789089] 2-1---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.177252 多线程[14462:6789088] 1-11---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177348 多线程[14462:6789088] 1-12---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177386 多线程[14462:6789088] 1-13---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177420 多线程[14462:6789088] 1-14---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177605 多线程[14462:6789088] 1-15---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177650 多线程[14462:6789088] 1-16---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177664 多线程[14462:6789089] 2-2---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.177807 多线程[14462:6789088] 1-17---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177855 多线程[14462:6789089] 2-3---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.177897 多线程[14462:6789088] 1-18---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.177939 多线程[14462:6789089] 2-4---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.177982 多线程[14462:6789088] 1-19---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.178287 多线程[14462:6789089] 2-5---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.178620 多线程[14462:6789088] 1-20---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.178678 多线程[14462:6789089] 2-6---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.178721 多线程[14462:6789088] 1-21---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.179618 多线程[14462:6789089] 2-7---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.180078 多线程[14462:6789088] 1-22---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.180354 多线程[14462:6789089] 2-8---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.180430 多线程[14462:6789088] 1-23---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.180476 多线程[14462:6789089] 2-9---<NSThread: 0x17406d700>{number = 4, name = (null)}
    2017-05-25 12:03:40.180518 多线程[14462:6789088] 1-24---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.180586 多线程[14462:6789088] 1-25---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.180619 多线程[14462:6789088] 1-26---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.180652 多线程[14462:6789088] 1-27---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182044 多线程[14462:6789088] 1-28---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182085 多线程[14462:6789088] 1-29---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182119 多线程[14462:6789088] 1-30---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182153 多线程[14462:6789088] 1-31---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182185 多线程[14462:6789088] 1-32---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182218 多线程[14462:6789088] 1-33---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182251 多线程[14462:6789088] 1-34---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182284 多线程[14462:6789088] 1-35---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182317 多线程[14462:6789088] 1-36---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182440 多线程[14462:6789088] 1-37---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182474 多线程[14462:6789088] 1-38---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182506 多线程[14462:6789088] 1-39---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182539 多线程[14462:6789088] 1-40---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182577 多线程[14462:6789088] 1-41---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182618 多线程[14462:6789088] 1-42---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182651 多线程[14462:6789088] 1-43---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182683 多线程[14462:6789088] 1-44---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.182715 多线程[14462:6789088] 1-45---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.190503 多线程[14462:6789088] 1-46---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.190563 多线程[14462:6789088] 1-47---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.190598 多线程[14462:6789088] 1-48---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.190806 多线程[14462:6789088] 1-49---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.190895 多线程[14462:6789090] +++++++++++++++++++++++++++
    2017-05-25 12:03:40.191051 多线程[14462:6789090] 3-0---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.191551 多线程[14462:6789090] 3-1---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.191607 多线程[14462:6789090] 3-2---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.191640 多线程[14462:6789090] 3-3---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.191672 多线程[14462:6789090] 3-4---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.191715 多线程[14462:6789088] 4-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192233 多线程[14462:6789090] 3-5---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.192313 多线程[14462:6789090] 3-6---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.192347 多线程[14462:6789090] 3-7---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.192379 多线程[14462:6789090] 3-8---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.192418 多线程[14462:6789090] 3-9---<NSThread: 0x170077e40>{number = 5, name = (null)}
    2017-05-25 12:03:40.192491 多线程[14462:6789088] 4-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192525 多线程[14462:6789088] 4-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192557 多线程[14462:6789088] 4-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192704 多线程[14462:6789088] 4-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192752 多线程[14462:6789088] 4-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.192920 多线程[14462:6789088] 4-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.193071 多线程[14462:6789088] 4-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.193108 多线程[14462:6789088] 4-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
    2017-05-25 12:03:40.193698 多线程[14462:6789088] 4-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}

    通过打印信息可以看到任务3和任务4是在任务1和任务2完全执行完之后才执行的
    
    我们经常使用图片下载框架SDWebImage里面就用到了这个栅栏函数来控制任务执行的顺序 具体代码可以在SDWebImage中搜索dispatch_barrier_async
    
    - dispatch_group 
    当我们需要监听一个并发队列中,所有任务都完成了,就可以用到这个group,因为并发队列你并不知道哪一个是最后执行的,所以以单独一个任务是无法监听到这个点的,如果把这些单任务都放到同一个group,那么,我们就能通过dispatch_group_notify方法知道什么时候这些任务全部执行完成了。
    
    

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, queue, ^{NSLog(@"0");});
    dispatch_group_async(group, queue, ^{NSLog(@"1");});
    dispatch_group_async(group, queue, ^{NSLog(@"2");});
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"down");});
    
    
    在例子中,我把3个log分别放在并发队列中,通过把这个并发队列任务统一加入group中,group每次runloop的时候都会调用一个方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用来检查group中的任务是否已经完成,如果已经完成了,那么会执行dispatch_group_notify的block,输出’down’看一下运行结果:
    
    

    2017-05-25 14:32:48.965429 多线程[14500:6812849] 0
    2017-05-25 14:32:48.965513 多线程[14500:6812849] 1
    2017-05-25 14:32:48.965533 多线程[14500:6812849] 2
    2017-05-25 14:32:49.067608 多线程[14500:6812807] down

    - dispatch_apply 快速迭代
    这个方法用于无序查找,在一个数组中,我们能开启多个线程来查找所需要的值
    
    NSArray *array=[[NSArray      alloc ]  initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6", nil];
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//创建队列
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zd---%@----%@",index, [array objectAtIndex:index], [NSThread currentThread]);
    });
    
    输出结果
    

    2017-05-25 14:40:23.350677 多线程[14505:6815659] 0---0---<NSThread: 0x174064bc0>{number = 1, name = main}
    2017-05-25 14:40:23.350668 多线程[14505:6815678] 1---1---<NSThread: 0x1700700c0>{number = 3, name = (null)}
    2017-05-25 14:40:23.350821 多线程[14505:6815659] 2---2---<NSThread: 0x174064bc0>{number = 1, name = main}
    2017-05-25 14:40:23.350962 多线程[14505:6815659] 4---4---<NSThread: 0x174064bc0>{number = 1, name = main}
    2017-05-25 14:40:23.351006 多线程[14505:6815659] 5---5---<NSThread: 0x174064bc0>{number = 1, name = main}
    2017-05-25 14:40:23.351049 多线程[14505:6815659] 6---6---<NSThread: 0x174064bc0>{number = 1, name = main}
    2017-05-25 14:40:23.351084 多线程[14505:6815678] 3---3---<NSThread: 0x1700700c0>{number = 3, name = (null)}

    可以看到参与迭代遍历的有子线程也有主线程,这里就要注意一个点:主线程也会参与迭代的过程,里面的任务是并发执行的,所以迭代时不能传主队列.
    
    - dispatch_once
       这个经常使用,创建单例的时候
       整个应用程序中只会执行一次
       本身是线程安全的,所以不用去担心线程安全的问题
            onceToken 是用来记录该部分代码是否被执行过  
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{ //第一次执行代码时 onecToken值为0,之后就都是 -1 
                        
        NSLog(@"--once---%@",[NSThread currentThread]);
    });
    
    
    #####线程安全与线程间通讯
    
    - 线程安全
    多线程安全隐患:
    资源共享,多个线程同时访问同一块资源,比如同时访问同一个对象,同一个变量,同一个文件, 这个时候就容易引发多线程安全的问题,容易引发数据错乱和数据安全的问题.
    
    解决方案:  加锁
    
    加互斥锁
    把相关代码 加在  @synchronized(objc){} 中,()中的objc称为锁对象,只要是唯一的就可以.当每个线程来执行加锁里面的代码时会先加把锁,执行完之后再解锁.这样下条线程会等待之前线程执行完之后,再去执行.
    
    举例
    

    @synchronized(self) {
    //1.检查余票
    NSInteger count = self.totalCount;
    if (count >0) {
    for (NSInteger i = 0; i<100000; i++) {
    //演示耗时操作
    }
    //2.卖出去一张票
    self.totalCount = count - 1;
    NSLog(@"%@卖出去了一张票,还剩下%zd张票",[NSThread currentThread].name,self.totalCount);
    }else
    {
    NSLog(@"票已经卖光了,做灰机回去吧~");
    break;
    }
    }

    注意点:
             1)锁定一份代码只能使用同一把锁,
             2)加锁的时候要注意位置
             3)加锁有前提条件:只有当多个线程存在抢夺同一块资源的时候才需要加锁
             4)加锁是需要耗费性能的
             5)造成线程同步(多个线程按照固定的顺序来执行任务)
    
    加同步锁 (NSLock)
    举例
    
        NSLock *lock = [[NSLock alloc] init];
        [lock lock];
        
        //1.检查余票
        NSInteger count = self.totalCount;
        if (count >0) {
            for (NSInteger i = 0; i<100000; i++) {
                //演示耗时操作
            }
            //2.卖出去一张票
            self.totalCount = count - 1;
            NSLog(@"%@卖出去了一张票,还剩下%zd张票",[NSThread currentThread].name,self.totalCount);
        }else
        {
            NSLog(@"票已经卖光了,做灰机回去吧~");
            break;
        }
       
        [lock unlock];
    
    AFNetWorking中用NSLock比较多一点 可以在AFNetWorking中搜索unlock方法进行查看具体使用
    
    其它的加锁方式还有:
    NSRecursiveLock :递归锁,有时候“加锁代码”中存在递归调用,递归开始前加锁,递归调用开始后会重复执行此方法以至于反复执行加锁代码最终造成死锁,这个时候可以使用递归锁来解决。使用递归锁可以在一个线程中反复获取锁而不造成死锁,这个过程中会记录获取锁和释放锁的次数,只有最后两者平衡锁才被最终释放。
    
    NSDistributedLock:分布锁,它本身是一个互斥锁,基于文件方式实现锁机制,可以跨进程访问。
    
    pthread_mutex_t:同步锁,基于C语言的同步锁机制,使用方法与其他同步锁机制类似。
    
    - 原子性和非原子性
    我们平时在开发中定义属性时有以下两种选择
    atomic:原子性,线程相对来说是安全的,因为它会为该属性的setter方法加锁,但是性能会影响
    nonatomic:非原子性,线程是不安全的,不会为setter方法加锁.性能会好
    关于这两个关键字的具体介绍参考下面的文章:
    https://www.douban.com/note/486901956/
    
    - 线程间通讯
    举个下载图片的例子
    GCD:
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSURL *url = [NSURL URLWithString:@"http://dimg07.c- ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
        
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        UIImage *image = [UIImage imageWithData:data];
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
        
    });
    
    
    NSThread:
    

    //开线程
    [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];

    download
    

    // 具体下载
    NSURL *url = [NSURL URLWithString:@"http://dimg07.c-ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];

    NSData *imageData = [NSData dataWithContentsOfURL:url];
    
    UIImage *image = [UIImage imageWithData:imageData];
    
    NSLog(@"dowbnload=====%@",[NSThread currentThread]);
    
    //4.回到主线程设置图片
    /*
     第一个参数:回到主线程要调用的方法
     第二个参数:回到主线程需要传递的参数 image
     第三个参数:要不要等待调用方法结束之后再继续往下走
     */
    //[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
    //NSLog(@"---end----");
    
    [self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
    
    //    [self.imageView  performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
    

    相关文章

      网友评论

          本文标题:iOS 多线程--GCD

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