iOS-GCD

GCD是一种易用、高效的多线程技术,可代替NSThread和performSelector使用,其性能高于NSThread。

创建/获取


GCD可创建有序执行的线程(串行线程)和无序线程(并行)两种。全局线程是所有应用程序共用的并行线程,实效性相对较差。串行线程会等待前一个线程执行完成再执行另一个线程。

dispatch_queue_t queue = dispatch_queue_create("MY_THREAD", NULL); // 串行线程
dispatch_queue_t queue = dispatch_queue_create("MY_THREAD", DISPATCH_QUEUE_CONCURRENT); // 并行线程
dispatch_queue_t queue = dispatch_get_main_queue(); // 主线程
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 全局线程(并行线程)

全局线程由于是所有应用程序公用的,因此它具有优先级参数。默认情况下使用_create方式创建的线程具有相同的优先级,如果需要改变某个线程的优先级,可采用如下方式:

dispatch_queue_t queue1 = dispatch_queue_create("MY_GCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(queue1, global);
// queue1的优先级被设置成跟global一样。

iOS6以下系统即使开启了ARC,在使用_create API创建线程后仍需要手动使用dispatch_release来释放线程。

dispatch_queue_t queue = dispatch_queue_create("MY_THREAD", NULL);
dispatch_async(queue, ^{ DO SOMETHING });
dispatch_release(queue);

第三行直接release是没问题的,不用担心block中的任务还没执行完。因为按照dispatch_async以及block的实现逻辑,线程会被复制到堆上并被block持有生成一个autorelease对象。

执行


dispatch_after
此API并不是指『立即将任务加入指定线程并在指定时间后执行任务』,而是『在指定时间后将任务加入指定线程』。由于受到线程状态等因素影响,任务实际执行时间是有延迟的。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1*NSEC_PER_SEC)), queue, ^{});
// 线程的实际执行时间应该在1秒到1+1/60秒之间,甚至更晚。

dispatch_async
异步执行,不会阻塞当前线程。

dispatch_sync
同步执行,使用此API要特别小心,如果传入的线程对象是『当前线程』,极有可能会引起线程死锁。

// 在主线程开启了sync方式,并传入主线程,必然死锁。
dispatch_sync(dispatch_get_main_queue(), ^{});

DISPATCH_GROUP


如果需要在N个任务执行完成之后,执行另一个任务M,除了使用串行线程外,还可以使用dispatch_group群组。

dispatch_queue_t queue1 = dispatch_queue_create("MY_GCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue1, ^{NSLog(@"1");});
dispatch_group_async(group, queue1, ^{NSLog(@"2");});
dispatch_group_async(group, queue1, ^{NSLog(@"3");});
dispatch_group_notify(group, queue1, ^{NSLog(@"Finish");});

如果想给群组设置一个超时时间,可使用dispatch_group_wait API。此API会返回在指定时间后尚未执行结束的任务数量,如果数量等于0即代表任务已全部完成。

dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1*NSEC_PER_SEC));
// 在1秒后时间后继续执行接下去的代码

DISPATCH_APPLY


APPLY API用于执行指定次数的线程任务,即便传入的线程类型是并行线程,此API也会等待所有任务都执行完成之后再继续执行后续代码。
一个应用场景是处理数组中每一个元素,假设每个元素都是独立的,产生的结果不会互相影响,使用此API的效率会比for循环高。for循环可以认为是串行方式。

dispatch_queue_t queue1 = dispatch_queue_create("MY_GCD", DISPATCH_QUEUE_CONCURRENT);
NSMutableArray * array = [NSMutableArray new];
dispatch_apply(array.count, queue1, ^(size_t index) {
    NSLog(@"%zu:%@",index, array[index]);
});
// APPLY结束后会继续执行接下去的代码

数据竞争


多线程必然遇到数据竞争的问题,即不同的任务对同一个资源进行了更新,由于执行时间不定可能导致结果不正确的问题。
对同一个资源除了考虑使用串行线程以外,也可以使用并行线程。使用并行线程时通过dispatch_barrier_async方法来执行『写』这个操作。

dispatch_queue_t queue1 = dispatch_queue_create("MY_GCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queuq1, blk_read1);
dispatch_async(queuq1, blk_read2);
dispatch_barrier_async(queue1, blk_write1);
dispatch_async(queuq1, blk_read3);
dispatch_barrier_async(queue1, blk_write2);
dispatch_async(queuq1, blk_read4);

write1任务会等待blk_read1、blk_read2任务执行后再执行,并且等待write1任务执行完成后才会执行read3任务。在read3执行后才会执行write2任务,在write2任务执行成功后才会执行read4。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 简介 GCD(Grand Central Dispatch)是在macOS10.6提出来的,后来在iOS4.0被引...
    sunmumu1222阅读 908评论 0 2
  • GCD笔记 总结一下多线程部分,最强大的无疑是GCD,那么先从这一块部分讲起. Dispatch Queue的种类...
    jins_1990阅读 790评论 0 1
  • 一. 重点: 1.dispatch_queue_create(生成Dispatch Queue) 2.Main D...
    BestJoker阅读 1,600评论 2 2
  • 一、GCD的API 1. Dispatch queue 在执行处理时存在两种Dispatch queue: 等待现...
    doudo阅读 514评论 0 0
  • 程序中同步和异步是什么意思?有什么区别? 解释一:异步调用是通过使用单独的线程执行的。原始线程启动异步调用,异步调...
    风继续吹0阅读 1,059评论 1 2