GCD详解

Friday, March 2, 2018

线程到底是什么

  • 1个 CPU 执行的 CPU 命令列为一条无分叉路径,即为线程。
  • 这种无分叉路径不止一条,存在多条时即为多线程。
  • 使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此看上去就好像1个 CPU 核能够并列的执行多个线程一样。
  • 具备多个 CPU 核的情况下,就是真的提供了多个 CPU 核并行执行多个线程的技术。
  • 使用多线程容易发生一些问题:多线程更新相同的资源会导致数据的不一致,停止等待事件的线程会导致多个线程相互持续等待,使用太多线程会导致消耗大量内存。

GCD 的 API

Dispatch Queue

  • Dispatch Queue 按照添加的顺序,先进先出执行处理。
  • 执行存在两种 Dispatch Queue,一种是等待现在执行中的 Serial Dispatch Queue,一种是不等待现在执行中处理的 Concurrent Dispatch Queue

dispatch_queue_create

<code class="language-objectivec">//以下代码生成了 Serial Dispatch Queue
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);

//生成 Concurrent Dispatch Queue
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
</code>
  • 虽然 Dispatch Queue 受到系统资源的限制,但是 dispatch_queue_create 函数可以生成多个任意的 Dispatch Queue

  • 虽然一个 Serial Dispatch Queue 同时只能执行一个追加处理,但是多个 Serial Dispatch Queue 可并行执行。

  • 大量生成 Serial Dispatch Queue 会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。

  • 只在避免多个线程更新相同资源导致数据竞争时使用 Serial Dispatch Queue

  • 对于 Concurrent Dispatch Queue 来说,不管生成多少,由于 XNU 内核只使用有效管理的线程,因此不会发生上述问题。

  • 生成的 Dispatch Queue 必须由程序员负责释放,这是因为 Dispatch Queue 并没有像 Block 那样具有作为 Objective-C 对象来处理的技术。

  • 通过 dispatch_queue_create 函数生成的 Dispatch Queue 在使用结束后通过dispatch_release 函数释放。

    //release dispatch_async(mySerialDispatchQueue, ^{}) dispatch_release(mySerialDispatchQueue)

    //retain dispatch_retain(myConcurrentDispatch)

  • 在 dispatch_async 函数中追加 Block 到 Dispatch Queue 后,即使立即释放 Dispatch Queue,该 Queue 由于被 Block 所持有也不会被废弃,所以 Block 能够执行。

Main Dispatch Queue / Global Dispatch Queue

  • 不用生成 Dispatch Queue,系统也有提供几个,那就是 Main Dispatch Queue 和 Global Dispatch Queue

  • Main Dispatch Queue 就是 Serial Dispatch Queue, Global Dispatch Queue 就是 Concurrent Dispatch Queue

  • Global Dispatch Queue 有四个优先级,High, Default, Low, Background

    //各种 Dispatch Queue 获取方法 //Main dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

    //Gloabl dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_Default, 0);

    dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

    dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

  • 对于这两个Dispatch Queue,执行 retain 和 release 函数不会引起任何变化,也不会有问题。

dispatch_set_target_queue

  • 通过 dispatch_queue_create 函数生成的DIspatch Queue默认都是 Default 优先级的

  • 变更优先度使用 dispatch_set_target_queue 函数

    dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create(“com.example.gcd.MySerialDispatchQueue”, NULL);

    dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

    //指定要变更优先级的 Dispatch Queue 为第一个参数,更改的优先级为第二个参数的优先级。 //除了变更优先级,还可以变更执行阶层,如果在多个 Serial Dispatch Queue 使用该函数变更为某一个 Serial Dispatch Queue,那么原本并行执行的多个 Serial Dispatch Queue,在目标 Serial Dispatch Queue 上只能同时执行一个处理。

  • 在必须将不可并行执行的处理追加到多个 Serial Dispatch Queue 中时,如果使用 set_target_queue 函数将目标指定为某一个 Serial Dispatch Queue,即可防止处理并行执行。

dispatch_after

  • 想在指定时间之后执行处理的,可以使用 dispatch after 函数实现

    //DISPATCH_TIME_NOW 表示现在的时间 //NSEC_PER_SEC 是毫微秒 //NSEC_PER_MSEC是毫秒 //dispatch_walltime 函数用于计算绝对时间

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), ^{ //Do something });

  • dispatch_after 函数并不是在指定时间后执行处理,而是在指定时间追加处理到 Dispatch Queue。

Dispatch Group

  • 在追加到 Dispatch Queue 中的多个处理全部结束后想执行结束处理,可以使用 Dispatch Group

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

    dispatch_group_async(group, queue, ^{//blk0 }); dispatch_group_async(group, queue, ^{//blk1 }); dispatch_group_async(group, queue, ^{//blk2 });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{//done}); dispatch_release(group);

    //执行结果可能如下 blk1, blk2, blk0, done

  • 无论向什么样的 Dispatch Queue 中追加处理,使用 Dispatch Group 都可监视这些处理执行的结束。

  • 创建的 group 需要通过 dispatch_release 函数释放。

    //也可以使用 dispatch_group_wait 函数等待全部处理执行结束 //DISPATCH_TIME_FORVER为一直等待 //也可以使用 dispatch_time_t 来创建等待时间

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC); long result = dispatch_group_wait(group, time); if (result == 0) { //已全部执行结束 }else { //dispatch group 还在执行中 }

dispatch_barrier_async

  • 为了高效率地进行访问,读取处理追加到 Concurrent Dispatch Queue中,写入处理在任意一个读取处理没有执行的状态下,追加到 Serial Dispatch Queue 中即可。可以用 dispatch_barrier_async 函数实现该功能。

    dispatch_async(queue, blk0_for_reading); dispatch_async(queue, blk1_for_reading); dispatch_async(queue, blk2_for_reading); dispatch_async(queue, blk3_for_reading); dispatch_barrier_async(queue, blk_for_writing); dispatch_async(queue, blk4_for_reading); dispatch_async(queue, blk5_for_reading); dispatch_async(queue, blk6_for_reading); dispatch_async(queue, blk7_for_reading);

![](/uploads/2018/03/WX20180302-112205@2x-300x131.png? imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • 使用 Concurrent Dispatch Queue 和 dispatch_barrier_async 函数可实现高效率的数据库访问和文件访问

dispatch_sync

  • async 意味着非同步,dispatch_async 函数不做任何等待

  • sync 意味着同步,在追加Block 结束之前,dispatch_sync 函数会一直等待。

    //以下代码在主线程中执行会发生死锁 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{NSLog(“Hello?)});

  • 尽可能的避免使用 dispatch_sync 函数,因为稍有不慎就会导致程序死锁。

dispatch_apply

  • dispatch_apply 该函数按指定的次数将指定的 Block 追加到指定的 Dispatch Queue 中,并等待全部处理执行结束。

  • 可以用来遍历数组

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //在 Global Dispatch Queue 中非同步执行

    dispatch_async(queue, ^{ //等待 dispatch_apply 函数全部处理完 dispatch_apply([array count], queue, ^(size_t index) { //并列处理包含在 NSArray 对象 });

    //全部处理完毕,在主线程非同步执行
    dispatch_async(dispatch_get_main_queue(), ^{
        //更新界面等操作
    });
    

    });

dispatch_suspend / dispatch_resume

  • 追加大量处理到 Dispatch Queue 时,有时希望不执行已追加的处理。

    //dispatch_suspend 函数挂起指定的 Dispatch Queue dispatch_suspend(queue);

    //dispath_resume 函数恢复指定的 Dispatch Queue dispatch_resume(queue);

  • 这些函数对已经执行的处理没有影响,挂起后,追加到 Dispatch Queue 中尚未执行的处理的在此之后停止执行。而恢复则使这些处理能够继续执行。

Dispatch Semaphore

<code class="language-objectivec">//考虑一种情况
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 10000; ++i) {
        dispatch_async(queue, ^{
                [array addObject:[NSNumber numberWithInt: i]];
        });
}
</code>
  • 上述代码,执行后由内存错误导致应用程序异常结束的概率很高。此时应该使用 DIspatch Semaphore。

  • DIspatch Semaphore 是持有计数的信号,该计数是多线程编程中的计数类型信号。所谓信号,类似于过马路时常用的手旗,可以通过时举起手旗,不可通过时放下手旗。而在 Dispatch Semaphore 中,使用计数来实现该功能。计数为0时等待,计数为1或大于等于1时,减去1而不等待。

  • 使用完必须通过 dispatch_release 函数释放

  • dispatch_semaphore_wait 函数等待 Dispatch Semaphore 的计数值达到大于等于1,达到时对该计数进行减法并从 dispatch_semaphore_wait 函数返回。

  • dispatch_semaphore_wait 函数返回0时,可安全地执行需要进行排他控制的处理。该处理结束时通过 dispatch_semaphore_signal 函数将 Dispatch Semaphore 的计数加1.

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //生成 dispatch semaphore,设置计数为1,保证可访问 NSmutableArray 类对象的线程同时只能有1个。 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); NSMutableArray *array = [[NSMutableArray alloc] init]; for(int i = 0; i < 10000; ++i){ dispatch_async(queue, ^{ //等待 Dispatch Semaphore,直到计数大于等于1 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

                //由于 Dispatch Semaphore 计数大于等于1,所以将计数减1,执行到此时,计数值恒为0,由于访问 NSMutableArray 类对象的线程只有1个,因此可以安全的更新
    
                [array addObject: [NSNumber numberWithInt: i]];
    
                //排他控制结束,通过 dispatch_semaphore_signal 函数将计数加1
                dispatch_semaphore_signal(semaphore);
    
        });
    

    }

    //使用结束,释放 semaphore dispatch_release(semaphore);

dispatch_once

<code class="language-objectivec">static dispatch_once_t pred;
dispatch_once(&pred, ^{
        //初始化
});
</code>
iOSObjective-C

View Animation

系统框架