Objective-C GCD深入理解
GCD(Grand Central Dispatch),主要用于多线程编程。它屏蔽了繁琐的线程实现及管理细节,将其交由系统处理。开发者只需要定义任务block(在底层被封装成dispatch_continuation_t结构体),并提交到正确的dispatch queue中。GCD包含dispatch queue和dispatch source。
一、dispatch queue是FIFO队列,分为两种:
1、serial(串行),队列中的block同一个时刻只会派发给一个线程,所以要等当前block执行完,才会开始执行下一个block。
2、concurrent(并行),队列中的block同一个时刻可能会派发给多个线程,所以多个block可以同时执行。
3、苹果官方说明:
Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue.
The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.
注意:串行队列中不同block可能在不同线程执行!
二、使用dispatch queue的方法,也分为两种:
1、dispatch_sync(同步),调用dispatch_sync方法的线程会被阻塞,直到block执行结束。在当前线程调用dispatch_sync方法可能会导致死锁!!!
2、dispatch_async(异步),调用dispatch_async方法的线程不会被阻塞。
NSLog(@"1"); // 死锁,只会输出1
dispatch_sync(currentQueue, ^{
NSlog(@"2"); // block也提交到当前线程对应的队列
});
NSlog(@"3");
死锁的理解:
1、dispatch_sync方法调用和block调用,是当前线程需要处理的两个任务。
2、dispatch_sync方法调用首先提交到队列中。
3、然后block调用提交到队尾,需要等待dispatch_sync方法调用完成。
4、而dispatch_sync方法调用,又需要等到block执行结束才能返回。这就形成了等待环,即死锁。
解决死锁:把block交由另一个线程执行
// 依次输出1、2、3
NSLog(@"1");
dispatch_sync(notCurrentQueue, ^{
NSlog(@"2"); // block提交到另外一个线程对应的队列
});
NSlog(@"3");
三、创建队列:
dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);
// label:队列的唯一标识,方便调试
// attr:队列类型,NULL或者DISPATCH_QUEUE_SERIAL表示串行,DISPATCH_QUEUE_CONCURRENT表示并行
dispatch_queue_t serialQueue = dispatch_queue_create("com.tencent.wechat.file", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.tencent.wechat.network",DISPATCH_QUEUE_CONCURRENT);
在MRC下,dispatch_queue_create创建的队列,需要用dispatch_release释放掉。
四、有时候,并不需要开发者创建队列,系统已经提供了两种很好用的队列。
主队列,对应主线程,是个串行队列:
dispatch_queue_t serialMainQueue = dispatch_get_main_queue();
全局队列,是并行队列:
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
// identifier:队列优先级
// flags:通常填0
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
五、dispatch_after用于延迟执行任务:
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
// 3秒后提交block到主队列,这个时间并不精确,得看当时线程的繁忙程度
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
六、dispatch_suspend和dispatch_resume:
1、dispatch_suspend,挂起队列,只会暂停还没执行的任务,已经在执行的任务不会被影响。
2、dispatch_resume,恢复队列,继续调度还没执行的任务。
3、注意,resume一个没有被suspend的队列会导致crash!
七、dispatch_once
详见:https://www.cnblogs.com/yangwenhuan/p/9603472.html
八、dispatch group
在多个并行block都执行完,这就可以用到dispatch group
九、dispatch_set_target_queue
修改优先级
十、dispatch_barrier_async
barrier,顾名思义,添加一个障碍,就是就是个同步点。
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// blk1, blk2, blk3并行执行
dispatch_async(globalConcurrentQueue, blk1);
dispatch_async(globalConcurrentQueue, blk2);
dispatch_async(globalConcurrentQueue, blk3);
// blk1, blk2, blk3全部执行完,才开始执行blk4
dispatch_barrier_async(globalConcurrentQueue, blk4);
// blk4执行完,才开始并行执行blk5, blk6
dispatch_async(globalConcurrentQueue, blk5);
dispatch_async(globalConcurrentQueue, blk6);
十一、dispatch_apply
提交特定数量的block到队列中,并等待全部执行结束。dispatch_apply存在与dispatch_sync一样的死锁问题,因此推荐把dispatch_apply放到dispatch_async中执行。
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, q, ^(size_t iter) {
NSLog(@"iteration[%zu]", iter);
});
NSLog(@"done"); 输出:
iteration[3]
iteration[2]
iteration[0]
iteration[4]
iteration[1]
iteration[5]
iteration[9]
iteration[8]
iteration[7]
iteration[6]
done // 最后输出一定是这个
十二、dispatch semaphore
dispatch semaphore是信号量的封装,可用于实现线程安全,相比串行队列,粒度更小
// 创建信号量,初始值可以不为1
dispatch_semaphore_t s = dispatch_semaphore_create(1);
// 阻塞等待信号量大于等于1
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
// 信号量减1,开始执行临界区代码 /* 临界区代码 */ // 临界区代码执行结束,信号量+1,最先等待信号量的线程得到执行
dispatch_semaphore_signal(s);
十三、dispatch io
// 获取文件描述符
dispatch_fd_t fd = open(file_path, O_RDWR);
// 创建用于处理dispatch io block的队列
dispatch_queue_t queue = dispatch_queue_create(queue_name, NULL);
// 创建dispatch io,绑定文件描述符
dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
// 异常
close(fd);
});
// 设置一次最多读取的字节数
dispatch_io_set_high_water(io, size);
// 这里书上说会使用global queue来并发读,但是实践没看出来,懂的同学请多多指教
dispatch_io_read(io, 0, max_size, queue, ^(bool done, dispatch_data_t _Nullable data, int error) {
// dispatch_data_t转NSString
dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
NSString *sData = [[NSString alloc] initWithBytes:buffer length:size encoding: NSUTF8StringEncoding];
return true;
});
});
十四、dispatch queue VS NSThread, pthread
利用NSThread和pthread也是可以进行多线程编程的,但是
1)需要自己实现线程管理,如分配多少线程、什么时候分配、什么时候销毁等等
2)性能很难比dispatch queue好,因为dispatch queue是基于XNU内核的workqueue实现的(中间还有一层封装是Libc中的pthread_workqueue)
十五、dispatch source
dispatch source是内核kqueue的封装。kqueue是一种多路复用技术,用于监听内核的各种事件通知,并做出相应处理。因为利用回调代替轮询,所以kqueue的CPU占用率很小。
// 创建source
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue);
// 取消source,已经在执行的handler会继续执行完,还没执行的不再执行
void dispatch_source_cancel(dispatch_source_t source);
// 事件source发生时,执行handler
void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t handler);
// 取消source时,执行handler
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t handler);
// 启动source
void dispatch_resume(dispatch_object_t object);
十六、dispatch queue和block的持有关系
block持有dispatch queue
参考链接:
Objective-C GCD深入理解的更多相关文章
- GCD 深入理解
GCD 深入理解(一) 虽然 GCD 已经出现过一段时间了,但不是每个人都明了其主要内容.这是可以理解的:并发一直很棘手,而 GCD 是基于 C 的 API ,它们就像一组尖锐的棱角戳进 Object ...
- GCD API 理解 (一)
资料先行 GCD 深入理解:第一部分 GCD 深入理解:第二部分 以上两篇文章是关于GCD讲的比较好的文章,翻译自raywenderlich,该网站有很多关于iOS 开发的优秀文章. 引子 iOS 开 ...
- GCD 深入理解(二)
转自@nixzhu的GitHub主页(译者:Riven.@nixzhu),原文<Grand Central Dispatch In-Depth: Part 2/2> 欢迎来到GCD深入理解 ...
- GCD深入理解(1)
写在前面 本文原文为raywenderlich的<grand-central-dispatch-in-depth-part-1>:顺便提及一下,笔者认为,对于iOS初学者而言,raywen ...
- GCD 深入理解:第一部分
虽然 GCD 已经出现过一段时间了,但不是每个人都明了其主要内容.这是可以理解的:并发一直很棘手,而 GCD 是基于 C 的 API ,它们就像一组尖锐的棱角戳进 Objective-C 的平滑世界. ...
- GCD 深入理解(一)
http://www.cocoachina.com/industry/20140428/8248.html 本文由@nixzhu翻译至raywenderlich的<grand-central-d ...
- IOS GCD 的理解
GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式. 在Mac OS X 10.6和IOS 4.0之后开 ...
- GCD 深入理解:第二部分
在本系列的第一部分中,你已经学到超过你想像的关于并发.线程以及GCD 如何工作的知识.通过在初始化时利用 dispatch_once,你创建了一个线程安全的 PhotoManager 单例,而且你通过 ...
- GCD的深入理解
GCD 深入理解(一) 本文由@nixzhu翻译至raywenderlich的<grand-central-dispatch-in-depth-part-1> 虽然 GCD 已经出现过一段 ...
随机推荐
- Placement of class definition and prototype
When I create a function, I can put the code for it after main if I put the prototype above main. Fo ...
- vue常用笔记
vue源码解析(勾三股四):http://jiongks.name/blog/vue-code-review/ 0.npm: https://www.npmjs.com/ 1. package.jso ...
- 使用git push命令如何忽略不想提交的文件夹或者文件
如下场景是在window下的操作. 在使用node的时候有个node_modules文件夹很大,一般情况下不想提交,忽略的办法如: 方法一(来自评论区):直接在仓库根目录:执行命令echo 'node ...
- HashMap底层实现原理(JDK1.8)源码分析
ref:https://blog.csdn.net/tuke_tuke/article/details/51588156 http://www.cnblogs.com/xiaolovewei/p/79 ...
- HTML--Canvas基础入门
一 HTML5画布基本介绍 1.HTML5专门为画布功能提供的标签:<canvas>,所以画布相关的功能都是基于这个标签来完成的; <canvas id="canvas&q ...
- java获取文件路径
情况:工程项目没有放到tomcat下,在eclipse运行 1.获取项目根目录,根据在哪里运行 2. 3.从最终生成的.class文件为着手点 4.在controller下
- CSS3圆圈动画放大缩小循环动画效果
代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" c ...
- VUE基于ElementUI搭建的简易单页后台
一.项目链接 GitHub 地址: https://github.com/imxiaoer/ElementUIAdmin 项目演示地址:https://imxiaoer.github.io/Eleme ...
- ThreadLocal终极源码剖析-一篇足矣!
本文较深入的分析了ThreadLocal和InheritableThreadLocal,从4个方向去分析:源码注释.源码剖析.功能测试.应用场景. 一.ThreadLocal 我们使用ThreadLo ...
- Android Studio 通过一个登录功能介绍SQLite数据库的使用
前言: SQLite简介:是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中.它是D.RichardHipp建立的公有领域项目.它的设计目标是嵌入式的,而且目前已经在 ...