GCD详解

作者: 码省理工0 | 来源:发表于2019-03-28 00:57 被阅读0次

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用.

    这个调度框架声明了几种数据类型和函数来创建和操作他们:
    一、调度队列
    所有的调度队列都是先进先出队列,因此,队列中的任务的开始的顺序和添加到队列中的顺序相同。GCD自动的为我们提供了一些调度队列,我们也可以创建新的用于具体的目的。
    下面列出几种可用的调度队列类型以及如何使用。
    二、调度资源
    它是一个监视某些类型事件的对象。当这些事件发生时,它自动将一个block放入一个调度队列的执行例程中。
    三、调度组
    允许将多任务分组来方便后来加入执行。任务能作为一个组中的一个成员被加到队列中,客户端能用这个组对象来等待直到这个组中的所有任务完成。
    四、调度信号量
    允许客户端并行发布一定数量的任务。

    队列queue和任务

    dispatch_async(queue,^{
    //想执行的任务
    });
    

    该源代码用block语法定义执行的任务,通过dispatch_async函数追加到queue的Dispatch Queue中。
    Dispatch Queue是执行处理的等待队列,通过dispatch_async或者dispatch_sync等函数API,在block语法中记述想指向的处理,并追加到Dispatch Queue。Dispatch Queue按照追加顺序处理。


    Snip20190327_42.png

    执行处理时候存在两种Dispatch Queue,一种是等待现在执行中的处理Serial Dispatch Queue,另一种不带带现在执行中处理的Concurrent Dispatch Queue。


    Snip20190327_43.png

    serial队列的特点:队列里面的任务按照顺序执行,一个任务完成,再去执行下一个,而且serial有两种创建方式

    concurrent队列的特点:队列里面的任务按照顺序开始执行,但是第二个任务不会等第一个任务结束才开始,以此类推,队列里面的任务执行完成顺序,可能跟进去队列的顺序不一致


    Snip20190327_44.png
    队列分类

    主队列:用于刷新UI,任何需要刷新UI的工作都要放在主队列中执行,同时为防止UI卡住,一般需要把耗时的任务放在其他线程中执行

    dispatch_queue_t queue = dispatch_get_main_queue();
    

    全局队列:一般并行任务都加入这个队列中,这是系统提供的一个全局的并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    获取程序进程缺省产生的并发队列,可根据优先级来选择高、中、低三个优先级,
    由于这个是全局有系统控制的队列,所以我们无法对其进行 dispatch_resume() 继续 和 dispatch_suspend() 中断。

    自定义队列:可以自定义串行队列或者并行队列
    //串行队列

    dispatch_queue_t queue = dispatch_queue_create("demo", NULL);
    dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
    //并行队列
    dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
    

    第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空
    第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列

    dispatch_sync(queue,block):同步队列,dispatch_sync 函数不会立即返回,会阻塞当前线程,将block放到指定的queue上面执行,等待该block同步执行完成后才会返回

    dispatch_async(queue,block):异步队列,dispatch_sync 函数会立即返回,block放到指定queue中等待执行,该block是并行还是串行只跟queue定义有关 ,

    同步(sync)异步(async)

    dispatch_sync(queue,block):同步队列,dispatch_sync 函数不会立即返回,会阻塞当前线程,将block放到指定的queue上面执行,等待该block同步执行完成后才会返回,只能在当前线程中执行任务,不具备开启新线程的能力。
    dispatch_async(queue,block):异步队列,dispatch_sync 函数会立即返回,block放到指定queue中等待执行,该block是并行还是串行只跟queue定义有关 ,可以在新的线程中执行任务,具备开启新线程的能力。

    同步异步串行并行组合方式

    区别 并发队列 串行队列 主队列
    同步 sync 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务
    异步 async 开启新线程,并行执行任务 开启新线程,串行执行任务 没有开启新线程,串行执行任务

    举个栗子

    • 同步+并发队列
    - (void)syncConcurrent {
        //创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3=====%@",[NSThread currentThread]);
            }
        });
        
    }
    
    
    Snip20190327_47.png
    • 同步+串行队列
    同步串行
    - (void)syncSerial {
        //串行队列
        dispatch_queue_t queue = dispatch_queue_create("syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3=====%@",[NSThread currentThread]);
            }
        });
    }
    
    
    Snip20190327_45.png
    • 同步+主队列
    - (void)syncmMainQueue {
        //创建并发队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2=====%@",[NSThread currentThread]);
            }
        });
        
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3=====%@",[NSThread currentThread]);
            }
        });
        
    }
    
    
    Snip20190327_48.png
        dispatch_queue_t quueu = dispatch_queue_create("com.muqi", DISPATCH_QUEUE_SERIAL);
        //添加任务..里面的任务会开辟一个线程
        dispatch_async(quueu, ^{
            NSLog(@"第1个任务%@",[NSThread currentThread]);
        });
        dispatch_async(quueu, ^{
            NSLog(@"第2个任务%@",[NSThread currentThread]);
        });
    
        dispatch_async(quueu, ^{
            NSLog(@"第3个任务%@",[NSThread currentThread]);
        });
    
        dispatch_async(quueu, ^{
            NSLog(@"第4个任务%@",[NSThread currentThread]);
        });
    
        dispatch_async(quueu, ^{
            NSLog(@"第5个任务%@",[NSThread currentThread]);
        });
    
        dispatch_async(quueu, ^{
            NSLog(@"第6个任务%@",[NSThread currentThread]);
        });
        dispatch_async(quueu, ^{
            NSLog(@"第7个任务%@",[NSThread currentThread]);
        });
        dispatch_async(quueu, ^{
            NSLog(@"第8个任务%@",[NSThread currentThread]);
        });
        dispatch_async(quueu, ^{
            NSLog(@"第9个任务%@",[NSThread currentThread]);
        });
        dispatch_async(quueu, ^{
            NSLog(@"第10个任务%@",[NSThread currentThread]);
        });
    

    异步+并发队列

    - (void)asyncConcurrent {
        dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent",DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3======%@",[NSThread currentThread]);
            }
        });
    }
    
    
    Snip20190327_49.png
    • 异步+串行队列
    - (void)asyncSerial {
        dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent",DISPATCH_QUEUE_SERIAL);
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3======%@",[NSThread currentThread]);
            }
        });
    }
    
    
    Snip20190327_50.png
    • 异步+主队列
    - (void)asyncmMainQueue {
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2======%@",[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3======%@",[NSThread currentThread]);
            }
        });
    }
    
    
    Snip20190327_51.png

    GCD 线程间的通信

    在开发当中,我们会将耗时操作放在子线程中运行,例如请求网络。拿到结果之后会在主线程刷新UI,

    - (void)threadNoti {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"执行耗时操作%@",[NSThread currentThread]);
            //模拟耗时操作
            [NSThread sleepForTimeInterval:3];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSLog(@"刷新UI%@",[NSThread currentThread]);
            });
        });
    }
    
    Snip20190327_53.png

    GCD死锁

    - (void)deadLock {
        NSLog(@"1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2");
        });
        NSLog(@"3");
    }
    

    输出结果

    Snip20190327_54.png
    调用栈上最后是DISPATCH_WAIT_FOR_QUEUE信息。说明最后是在等待队列中,为什么会是这样,逐步分析。
    • dispatch_sync表示是一个同步线程;
    • dispatch_get_main_queue表示运行在主线程中的主队列;
    • 任务2是同步线程的任务。
    • 任务3需要等待任务2结束之后再执行.

    首先执行任务1,这是肯定没问题的,只是接下来,程序遇到了同步线程,那么它会进入等待,等待任务2执行完,然后执行任务3。但这是主队列,是一个特殊的串行队列,有任务来,当然会将任务加到队尾,然后遵循FIFO原则执行任务。那么,现在任务2就会被加到最后,任务3排在了任务2前面,那么问题来了

    它们处在同一个串行队列中,任务2需要等待任务3完成,任务3需要等待任务2完成,一直僵持卡在那里了。也就是传说中的死锁。


    1483059-82244285841f9e60.png

    我们可以用async+并发队列来打破修改后代码如下

    - (void)deadLock {
        NSLog(@"1");
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"2");
        });
        NSLog(@"3");
    }
    

    运行结果


    Snip20190327_55.png

    核心点在于有没有在当前任务中代码,添加一个同步串行任务

    Dispatch Group

     1、dispatch_group_create创建一个调度任务组
     2、dispatch_group_async 把一个任务异步提交到任务组里
     3、dispatch_group_enter/dispatch_group_leave 这种方式用在不使用dispatch_group_async来提交任务,且必须配合使用
     4、dispatch_group_notify 用来监听任务组事件的执行完毕
     5、dispatch_group_wait 设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败
    
    要求:异步+并发执行两个任务,任务完毕后回到主线程。
    
    - (void)groupNotify {
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_t groupQueue = dispatch_group_create();
        dispatch_group_async(groupQueue, queue, ^{
            NSLog(@"任务1%@",[NSThread currentThread]);
    
        });
        dispatch_group_async(groupQueue, queue, ^{
            NSLog(@"任务2%@",[NSThread currentThread]);
        });
        dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{
            NSLog(@"主线程%@",[NSThread currentThread]);
        });
      
    }
    

    dispatch_group_enter,dispatch_group_leave

    • dispatch_group_enter :通知 group,下个任务要放入 group 中执行了
    • dispatch_group_leave: 通知 group,任务成功完成,要移除,与 enter成对出现
    - (void)grouEnter{
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_t groupQueue = dispatch_group_create();
        
        dispatch_group_enter(groupQueue);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"任务1%@",[NSThread currentThread]);
            dispatch_group_leave(groupQueue);
        });
        dispatch_group_enter(groupQueue);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"任务2%@",[NSThread currentThread]);
            dispatch_group_leave(groupQueue);
        });
        
    //    dispatch_group_enter(groupQueue);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:5];
            NSLog(@"任务3%@",[NSThread currentThread]);
    //        dispatch_group_leave(groupQueue);
        });
    //    dispatch_group_enter(groupQueue);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:5];
            NSLog(@"任务4%@",[NSThread currentThread]);
    
    //        dispatch_group_leave(groupQueue);
        });
        dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{
            NSLog(@"主线程%@",[NSThread currentThread]);
        });
      
    }
    

    结果如下


    Snip20190327_57.png

    任务1,2加入到任务组中,任务3,4没有。所以只要任务1,2完成,就会回到主线程。

    dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
    参数可以设置超时时间),在group上任务完成前,dispatch_group_wait会阻塞当前线程(所以不能放在主线程调用)一直等待;当group上任务完成,或者等待时间超过设置的超时时间会结束等待;

    /**
     * 队列组 dispatch_group_wait
     */
    - (void)groupWait {
        NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
        NSLog(@"group---begin");
        
        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) {
                [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
                NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
            }
        });
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 追加任务2
            for (int i = 0; i < 2; ++i) {
                [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
                NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
            }
        });
        
        // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        
        NSLog(@"group---end");
    }
    
    
    Snip20190327_58.png

    GCD 的其他方法

    1 dispatch_barrie

    一个dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时,barrier block自己开始执行。 之后, 队列继续正常的执行操作。
    调用这个函数总是在barrier block被提交之后立即返回,不会等到block被执行。当barrier block到并发队列的最前端,他不会立即执行。相反,队列会等到所有当前正在执行的blocks结束执行。到这时,barrier才开始自己执行。所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。
    这里指定的并发队列应该是自己通过dispatch_queue_create函数创建的。如果你传的是一个串行队列或者全局并发队列,这个函数等同于dispatch_async函数。

    20150726170216381.png

    dispatch_barrier_sync

    - (void)dispatch_barrier_sync {
        NSLog(@"start");
    
        //获取全局并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"任务1");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务2");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务3");
        });
        dispatch_barrier_sync(queue, ^{
            NSLog(@"任务 1,2,3完成我才开始");
            [NSThread sleepForTimeInterval:3];
        });
    //    dispatch_barrier_async(queue, ^{
    //        NSLog(@"任务 1,2,3完成我才开始");
    //        [NSThread sleepForTimeInterval:3];
    //    });
        NSLog(@"=====");
        dispatch_async(queue, ^{
            NSLog(@"任务4");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务5");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务6");
        });
        NSLog(@"end");
    }
    
    Snip20190327_59.png

    dispatch_barrier_async

    - (void)dispatch_barrier_async {
        NSLog(@"start");
    
        //获取全局并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"任务1");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务2");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务3");
        });
    //    dispatch_barrier_sync(queue, ^{
    //        NSLog(@"任务 1,2,3完成我才开始");
    //        [NSThread sleepForTimeInterval:3];
    //    });
        dispatch_barrier_async(queue, ^{
            NSLog(@"任务 1,2,3完成我才开始");
            [NSThread sleepForTimeInterval:3];
        });
        NSLog(@"=====");
        dispatch_async(queue, ^{
            NSLog(@"任务4");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务5");
        });
        dispatch_async(queue, ^{
            NSLog(@"任务6");
        });
        NSLog(@"end");
    }
    
    
    Snip20190327_60.png

    dispatch_barrier_sync 需要等待栅栏执行完才会执行栅栏后面的任务,而dispatch_barrier_async 无需等待栅栏执行完,会继续往下走(保留在队列里)

    Dispatch Semaphore

    信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
    其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

    //创建信号量,参数:信号量的初值,如果小于0则会返回NULL
    dispatch_semaphore_create(信号量值)
     
    //等待降低信号量
    dispatch_semaphore_wait(信号量,等待时间)
     
    //提高信号量
    dispatch_semaphore_signal(信号量)
    
    -(void)dispatchSignal{
        //crate的value表示,最多几个资源可访问
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
        dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //任务1
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 1");
            sleep(1);
            NSLog(@"complete task 1");
            dispatch_semaphore_signal(semaphore);
        });
        //任务2
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 2");
            sleep(1);
            NSLog(@"complete task 2");
            dispatch_semaphore_signal(semaphore);
        });
        //任务3
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 3");
            sleep(1);
            NSLog(@"complete task 3");
            dispatch_semaphore_signal(semaphore);
        });
    }
    
    
    Snip20190327_61.png

    同时只执行了一个任务,修改dispatch_semaphore_t semaphore = dispatch_semaphore_create(2)看执行结果

    -(void)dispatchSignal{
        //crate的value表示,最多几个资源可访问
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
        dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //任务1
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 1");
            sleep(1);
            NSLog(@"complete task 1");
            dispatch_semaphore_signal(semaphore);
        });
        //任务2
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 2");
            sleep(1);
            NSLog(@"complete task 2");
            dispatch_semaphore_signal(semaphore);
        });
        //任务3
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 3");
            sleep(1);
            NSLog(@"complete task 3");
            dispatch_semaphore_signal(semaphore);
        });
    }
    
    
    Snip20190327_62.png

    dispatch_after

    dispatch_after是来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用dispatch_after是最合适的
    dispatch_after能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch_queue

    - (void)dispatchAfter {
        NSLog(@"开始");
        //该方法的第一个参数是time,第二个参数是dispatch_queue,第三个参数是要执行的block。
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"延迟执行%@",[NSThread currentThread]);
        });
        NSLog(@"结束");
    }
    
    Snip20190327_63.png

    上面这句dispatch_after的真正含义是在3秒后把任务添加进队列中,并不是表示在3秒后执行,大部分情况该函数能达到我们的预期,只有在对时间要求非常精准的情况下才可能会出现问题。

    dispatch_once

    执行某个事件一次且仅只有一次。
    它只有两个参数,第一个参数是once,第二个是首次执行的block块,调用起来是这样的

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self demodispatch_once];
        [self demodispatch_once];
    
    }
    - (void)demodispatch_once {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"执行一次");
        });
    }
    
    Snip20190328_65.png

    这里有坑

    - (void)demodispatch_once {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"A执行一次");
            dispatch_once(&onceToken, ^{
                NSLog(@"B执行一次");
            });
        });
    }
    
    Snip20190328_66.png

    dispatch_once_f函数造成了信号量的永久等待

    dispatch_once不止是简单的执行一次,如果再次调用会进入非首次更改的模块,如果有未DONE的请求会被添加到链表中
    2、所以dispatch_once本质上可以接受多次请求,会对此维护一个请求链表
    3、如果在block执行期间,多次进入调用同类的dispatch_once函数(即单例函数),会导致整体链表无限增长,造成永久性死锁
    详情请参考单例滥用 - 滥用单例dispatch_once而造成的死锁问题

    dispatch_apply

    作用是把指定次数指定的block添加到queue中, 第一个参数是迭代次数,第二个是所在的队列,第三个是当前索引,dispatch_apply可以利用多核的优势,所以输出的index顺序不是一定的
    dispatch_apply,异步+并发执行10次。

    - (void)apply {
        dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t t) {
            NSLog(@"%zu===%@",t,[NSThread currentThread]);
        });
    
    }
    
    Snip20190328_67.png

    dispatch_apply,同步+串行执行10次。

    - (void)apply {
        dispatch_apply(10, dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL), ^(size_t t) {
            NSLog(@"%zu===%@",t,[NSThread currentThread]);
        });
    
    }
    
    Snip20190328_68.png

    相关文章

      网友评论

          本文标题:GCD详解

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