day23 - GCD
队列的种类:
共2种:
串行 | Serial |
并行 | Concurrent |
线程和队列的关系:
-
线程需要一个队列用于存放任务,否则任务一股脑的进入线程,将无法单一顺序执行
-
队列需要依托于一个线程来将其内部储存的任务消化掉。否则堆积了无数的任务却无法被执行。
dispatch_queue_t 定义:
dispatch_queue_t 本质上是 dispatch_queue_s,dispatch_queue_s继承自dispatch_object_s
主程序的 Main 队列如何创建的?
其中,DQF_WIDTH(1) 决定了队列的并发任务数量。此处为 1,则为串行队列。
而 dq_serialnum 则是作为标识去做记录的一个字段,此处 dq_serialnum = 1,是对主队列的特殊标识。
各个数值含义如下:
小面试题:
1、下方代码输出什么?
1 while (self.num < 5) { 2 dispatch_async(dispatch_get_global_queue(0 ,0), ^{ 3 self.num++; 4 }); 5 } 6 NSLog(@"%d", self.num);
2、下方代码输出什么?
1 for (int i = 0; i < 10000; i++) { 2 dispatch_async(dispatch_get_global_queue(0 ,0), ^{ 3 self.num++; 4 }); 5 } 6 NSLog(@"%d", self.num);
答案:
1、>= 5
2、<= 10000
解释:
线程安全。
1 中的while判断条件是基于一个属性值来判断的,中间是将任务提交到并发队列异步执行,并且没有做资源保护,导致当队列执行任务较慢时,会有大于5个任务符合条件提交到队列中。此时较早提交的任务还没有对 num 完成值更改,导致最终 num 执行 ++ 数量过多。
2 中的 for 循环限制了最终 num 执行 ++ 数量,没有冲突的情况结果会是10000。由于没有做资源保护,会有不同任务有读取到 num 值为相同的情况存在,则导致多个任务执行 num++ 的结果为相同值,使得最终结果 <10000。
好了,在了解了dispatch并小试牛刀之后,我们可以考虑一个问题:dispatch_sync/dispatch_async 中的 Block 是何时、怎么调用的呢?
dispatch_sync() 的调用过程分析:
1、从调用处开始作为入口
看到 block 类型的 work 变量,传入 _dispatch_Block_invoke()
void *ctxt = work
dispatch_function_t = _dispathc_Block_invoke(work) 分别作为入参进入下层。
2、对 block 中的具体实现强转,函数
使用 _dispatch_sync_f() 调用
3、再转到 _dispatch_sync_f_inline()
4、由于无法知道上图中具体调用哪个判断分支,使用符号断点,发现是走的 _dispatch_sync_f_slow()
5、再转入 _dispatch_sync_function_invoke()
6、再转入 _dispatch_sync_function_invoke_inline()
7、再转入_dispatch_client_callout(),实现如下:
文件名 | object.m | init.c |
实现 |
_dispatch_Block_invoke() 是一个宏定义的,如下:
则,最终的实现为 block->invoke(block);
最终在调用栈上的体现:
dispatch_async()