美文网首页
iOS GCD 串行(serial)/并发(concurrent

iOS GCD 串行(serial)/并发(concurrent

作者: iOS坚持者 | 来源:发表于2019-06-20 22:40 被阅读0次

    欢迎私信探讨。

    • GCD中队列分为两种
      • 串行队列:
        串行队列的特点是队列中的任务会一个一个的执行,前一个任务不执行完成下一个任务就不会执行。
        创建方式:
        dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
      • 并发队列:
        并发队列的特点是不管你多少的任务都会同时执行,任务一般并发队列和异步方式配对使用。
        创建方式:
        dispatch_queue_t queue = dispatch_queue_create("并发", DISPATCH_QUEUE_CONCURRENT);
    • 线程执行方式也分为两种
      • 同步执行:
        同步执行的特点是,不具备开启新线程能力,且会阻塞当前线程。
        调用方式:
        dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
      • 异步执行:
        异步执行的特点是,具备开启新线程能力,不会阻塞当前线程;但是只是说它具备这个能力,并不是一定会开启子线程,因为在主队列中异步执行依然会是在主线程中执行。
        调用方式:
        dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)

    接下来将通过串行并发同步异步穿插介绍其中原理。无特殊说明默认在主线程中

    serial sync 执行
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
        NSLog(@"0000");
    

    输出:

    2019-06-19 17:49:34.547 fasfsdfs[21016:325617] 开始----<NSThread: 0x6000018ac140>{number = 1, name = main}
    2019-06-19 17:49:36.548 fasfsdfs[21016:325617] 结束----<NSThread: 0x6000018ac140>{number = 1, name = main}
    2019-06-19 17:49:36.548 fasfsdfs[21016:325617] 0000
    

    分析

    1. 打印第一句:因为是同步,所以会阻塞主线程去执行 queue 队列中的任务,输出第一句,由打印0000的时间和打印开始----的时间对比可得。
    2. 接下来的输出都是按部就班的打印了,没有什么好深入的。
    serial sync 任务中嵌套 sync 任务
        dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
            });
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
        NSLog(@"0000");
    

    输出:

    2019-06-19 18:11:26.510 fasfsdfs[21508:339513] 开始----<NSThread: 0x600003ce1540>{number = 1, name = main}
    

    分析:

    1. 输出开始---是正确的,上面分析过
    2. 如果你对死锁有了解或者跑了代码就会发现,程序会crash,因为造成了线程死锁。这一点需要重点讲解一下。

    这里发生死锁的原因是:在串行队列中同步任务中嵌套了一个新的同步任务。

    根据串行队列中任务是一个一个的执行,同步执行是会阻塞线程的,当执行到第二个dispatch_sync时,它阻塞线程去等待第一个dispatch_sync里面的任务先去执行完,而第二个同步任务却是在第一个同步任务里面,只有第二个同步执行完了才会继续执行接下来的代码,这样你看着我,我看着你,双方都卡住了,就造成了死锁。

    serial sync 任务中嵌套 async 任务
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
    
        dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-19 18:37:54.614 fasfsdfs[21713:343984] 开始----<NSThread: 0x6000024b94c0>{number = 1, name = main}
    2019-06-19 18:37:56.615 fasfsdfs[21713:343984] 结束----<NSThread: 0x6000024b94c0>{number = 1, name = main}
    2019-06-19 18:37:56.615 fasfsdfs[21713:355085] 111----<NSThread: 0x600002426640>{number = 14, name = (null)}
    

    分析:

    1. 串行任务依次执行,当执行到 async 时需要等当前的任务执行完成才回去执行里面的任务,因为是异步的所以不会阻塞线程,且不是在主队列中所以会开起子线程执行,综上所述不会造成死锁。
    serial async 任务中嵌套 sync
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
    
        dispatch_async(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
            });
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
        NSLog(@"0000");
    

    输出:

    2019-06-20 16:44:02.738 fasfsdfs[34323:745553] 开始----<NSThread: 0x600003558e80>{number = 5, name = (null)}
    

    分析:

    1. 虽然是在异步调用的子线程中执行 sync 任务,但是依然会在调用 synccrash。原理同上,串行队列任务一个个执行,上一个任务不执行完成,下一个任务就不会执行,而 sync 属于任务嵌套中的任务。
    serial async 任务中嵌套 async
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
    
        dispatch_async(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-20 17:21:28.635 fasfsdfs[35506:772778] 开始----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
    2019-06-20 17:21:30.639 fasfsdfs[35506:772778] 结束----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
    2019-06-20 17:21:30.639 fasfsdfs[35506:772778] 111----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
    

    分析:

    1. 串行任务,任务依次执行,异步不会阻塞线程,具备开启线程能力,所以在第二个 async 时会等当前任务执行完成再去执行,由打印的时间和次序对比支持以上结论。
    concurrent sync
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    NSLog(@"0000");
    

    输出:

    2019-06-20 17:30:12.118 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
    2019-06-20 17:30:14.118 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
    2019-06-20 17:30:14.118 fasfsdfs[35506:772690] 0000
    

    分析:

    1. 并发队列任务可以同时执行,同步不具备开启线程能力,会阻塞当前线程,由打印数据可以得出结论。
    concurrent sync 任务中嵌套 sync
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-20 17:32:13.585 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
    2019-06-20 17:32:13.585 fasfsdfs[35506:772690] 111----<NSThread: 0x60000014ee00>{number = 1, name = main}
    2019-06-20 17:32:15.586 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
    

    分析:

    1. 并发与串行的差别在这里可以提现到淋漓尽致。如果串行这样执行,必然会造成死锁。
    2. 执行第一个 sync 时,阻塞主线程,暂停正在执行的任务,转而去执行sync 里面的任务。
    3. 执行到第二个sync时,如果是串行任务之间会互相等待,而并发却不会,新任务不必等待之前的任务执行完;所以第二个 sync会暂停当前任务去执行自己block 里面的任务。
    4. 任务二执行完成,主线程继续执行接下来的任务,等待两秒,打印输出。
    concurrent sync 任务中嵌套 async
    dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
                sleep(1);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-20 22:21:33.544 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
    2019-06-20 22:21:33.544 fasfsdfs[35506:811697] 111----<NSThread: 0x600000130c80>{number = 11, name = (null)}
    2019-06-20 22:21:35.545 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
    

    分析:

    1. 如果前面所讲您已经了解明白,我相信这一步不会是问题的。
    2. sync 主线程执行,async 并发队列开启子线程执行新任务

    #######concurrent async

        dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_async(queue, ^{
            
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
        NSLog(@"0000");
    

    输出:

    2019-06-20 22:26:57.317 fasfsdfs[35506:772690] 0000
    2019-06-20 22:26:57.317 fasfsdfs[35506:822749] 开始----<NSThread: 0x6000001dc140>{number = 17, name = (null)}
    2019-06-20 22:26:59.318 fasfsdfs[35506:822749] 结束----<NSThread: 0x6000001dc140>{number = 17, name = (null)}
    

    分析:

    1. "0000" 打印在 "开始" 之前,我猜测可能是系统内部要创建开辟内存耗时了。
    2. 异步开启子线程
    concurrent async 任务中嵌套 sync
    dispatch_async(queue, ^{
            
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
                sleep(1);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-20 22:33:16.687 fasfsdfs[35506:825627] 开始----<NSThread: 0x600000130500>{number = 20, name = (null)}
    2019-06-20 22:33:16.687 fasfsdfs[35506:825627] 111----<NSThread: 0x600000130500>{number = 20, name = (null)}
    2019-06-20 22:33:19.694 fasfsdfs[35506:825627] 结束----<NSThread: 0x600000130500>{number = 20, name = (null)}
    

    分析:

    1. async 开启子线程,sync 阻塞线程,去执行自己的任务。
      concurrent async 任务中嵌套 async
    dispatch_async(queue, ^{
            
            //打印 NSThread为main
            NSLog(@"开始----%@",[NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"111----%@",[NSThread currentThread]);
                sleep(1);
            });
            sleep(2);
            NSLog(@"结束----%@",[NSThread currentThread]);
        });
    

    输出:

    2019-06-20 22:37:48.393 fasfsdfs[35506:830404] 开始----<NSThread: 0x6000001dee80>{number = 21, name = (null)}
    2019-06-20 22:37:48.394 fasfsdfs[35506:830834] 111----<NSThread: 0x6000001def40>{number = 22, name = (null)}
    2019-06-20 22:37:50.397 fasfsdfs[35506:830404] 结束----<NSThread: 0x6000001dee80>{number = 21, name = (null)}
    

    分析:

    1. 执行到第一个 async,因为是并发,开启一个新线程去执行任务。
    2. 执行到第二个 async,又开启新的线程去执行新的任务。

    上面就是 并发/串行, 同步/异步 的各种嵌套分析;如果还有不清楚的读者欢迎私信我。

    相关文章

      网友评论

          本文标题:iOS GCD 串行(serial)/并发(concurrent

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