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

2019-06-20  本文已影响0人  iOS坚持者

欢迎私信探讨。

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

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,又开启新的线程去执行新的任务。

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

上一篇下一篇

猜你喜欢

热点阅读