GCD

作者: __拼搏__ | 来源:发表于2020-01-18 16:56 被阅读0次

    两个特殊队列:

    dispatch_get_main_queue()主队列,串行队列。
    dispatch_get_global_queue(0, 0)全局并发队列。这个队列是系统维护的,会被用来处理很多系统级事件。

    最简单的block:

    dispatch_block_t这是个无参无返回值的block。

    关于同步异步函数:

    dispatch_sync同步函数没有开启线程的能力。所有的代码都会在当前线程立即执行。
    dispatch_async异步函数有开启线程的能力。

    关于串行并行队列:

    dispatch_queue_create(0,0)
    DISPATCH_CURRENT_QUEUE_LABEL串行队列遵循FIFO原则,先进先出。
    DISPATCH_QUEUE_CONCURRENT并行队列,之间不会相互影响会各自执行。执行顺序与加入队列顺序有关。
    排列组合之后,就有了这么一套机制,如下图:

    image.png

    知识点较多的主要是同步函数串行队列。产生堵塞的原因本质上还是任务执行顺序的问题。如下经典代码就会产生堵塞(死锁):

     // 同步队列
        dispatch_queue_t queue = dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"2");
            // 同步
            dispatch_sync(queue, ^{
                 NSLog(@"3");
            });
              NSLog(@"4");
        });
        NSLog(@"5");
    

    分析图如下


    image.png

    本质上,异步函数里的代码是加入队列后,按顺序执行的。所以会执行2->同步->4
    但是,同步函数的性质又会让代码立即执行。所以在执行同步函数的时候,会要求立即执行log3
    但是log3这个任务因为串行队列顺序的原因,必须等到log4执行完毕之后才会执行。
    此时发生了log4log3相互等待的情况,而产生了堵塞。

    这只是同步串行会出现问题的一种方式。单纯的在主线程中使用同步串行,是没有问题,而且借助其一定会按顺序执行的特性。还能达到某些锁的功能:如经典的购票问题:

    - (void)saleTickes {
        self.tickets = 20;
        _queue = dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);
        while (self.tickets > 0) {
            // 使用串行队列,同步任务卖票
            dispatch_sync(_queue, ^{
                // 检查票数
                if (self.tickets > 0) {
                    self.tickets--;
                }
            });
        }
    }
    

    代码很简单,不需要解释。这个demo只是说明同步串行的效果。其实这段代码意义不大,因为实现购票必定要在异步并发队列里才会更好的达到效果。如下:

    - (void)saleTickes {
        NSLock *lock = [NSLock new];
        _tickets = 20;
        _queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_CONCURRENT);
        while (self.tickets > 0) {
            [lock lock];
            dispatch_async(_queue, ^{
                // 检查票数
                if (self.tickets > 0) {
                    self.tickets--;
                    NSLog(@"还剩 %zd %@", self.tickets, [NSThread currentThread]);
                } else {
                    NSLog(@"没有票了");
                }
                [lock unlock];
            });
        }
    }
    
    

    栅栏函数:

    栅栏函数的用法,多用于控制并发队列的执行时机,并且只用于控制唯一一个并发队列(控制串行队列没有意义)。其中的barrier单词很好的说明了他的作用。就是个挡路的:在我之前的任务都要被我挡住,等我执行完毕之后,之后的任务才会执行
    dispatch_barrier_sync同步栅栏函数:不仅仅会阻挡 并发队列的任务,还会阻挡 当前线程的任务 。直至该函数 之前的并发队列任务 执行完毕后,才会继续执行 当前线程的任务后面的并发任务
    dispatch_barrier_async异步栅栏函数:只阻挡 并发队列的任务 。不会阻挡 当前线程的任务 。所以 当前线程的任务 ,都会比 所有的并发队列的任务 先执行。
    demo如下:就不解释了

    - (void)demo2{
        dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
        /* 1.异步函数 */
        dispatch_async(concurrentQueue, ^{
            for (NSUInteger i = 0; i < 5; i++) {
                NSLog(@"download1-%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        dispatch_async(concurrentQueue, ^{
            for (NSUInteger i = 0; i < 5; i++) {
                NSLog(@"download2-%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        /* 2. 栅栏函数 */
        dispatch_barrier_sync(concurrentQueue, ^{
            NSLog(@"---------------------%@------------------------",[NSThread currentThread]);
        });
        NSLog(@"加载那么多,喘口气!!!");
        /* 3. 异步函数 */
        dispatch_async(concurrentQueue, ^{
            for (NSUInteger i = 0; i < 5; i++) {
                NSLog(@"日常处理3-%zd-%@",i,[NSThread currentThread]);
            }
        });
        NSLog(@"**********起来干!!");
        
        dispatch_async(concurrentQueue, ^{
            for (NSUInteger i = 0; i < 5; i++) {
                NSLog(@"日常处理4-%zd-%@",i,[NSThread currentThread]);
            }
        });
    }
    

    调度组:

    用这个也可以控制任务调度顺序。用法如下:

     - (void)groupDemo {
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"第一个走完了");
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"第二个走完了");
            dispatch_group_leave(group);
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"所有任务完成,可以更新UI");
            
        });
    }
    

    注意:
    1.dispatch_group_enterdispatch_group_leave需要成对出现。
    2.dispatch_group_enter 多于 dispatch_group_leave 不会调用通知
    3.dispatch_group_enter 少于 dispatch_group_leave 会奔溃
    4.所有的dispatch_group_enter都要在dispatch_group_notify之前执行。

    相关文章

      网友评论

          本文标题:GCD

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