NonMac

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() 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2021-08-09 16:52  NonMac  阅读(48)  评论(0编辑  收藏  举报

导航