dispatch_async 的分析

dispatch_async 在 libdispatch 是以 dispatch_async_f 来展示的。


dispatch_async_f

在这个函数中,我们的 block 被封装成的 dispatch_continuation_t 结构,在这个函数还应用了懒加载的方式来使用 dispatch_continuation_t,首先它从线程的TLS(线程的私有存储,如果了解过 pthread 的同学们应该明白,线程都是有自己的私有存储的,这些私有存储不会被其他线程所使用,这里不清楚也无所谓,反正就是创建了一个 dispatch_continuation_t 数据)中提取出一个 dispatch_continuation_t 结构,如果线程中的TLS不存在 dispatch_continuation_t 结构的数据,则从堆中获取,这也就是 _dispatch_continuation_alloc_cacheonly() 和 _dispatch_async_f_slow() 函数的作用。 

接下来,是将任务压进我们传进来的队列中,这里要详细讲述 _dispatch_queue_push 函数及其接下来的逻辑:


_dispatch_queue_push_list

_dispatch_queue_push_list 中,判断传入的队列是否有内容,如果有,直接接在队列后面,如果没有,则将内容接在队列头,然后唤醒这个队列,注意我说的是内容,而不是任务,因为有时候使用这个函数压进去不一定是任务,也有可能是队列,没错,将队列压进队列中,其实不能说压,说接这个动词,更恰当一点。_dispatch_queue_push_list 函数用得还是 inline 函数,说明这个函数会调用很频繁,inline 通常用在内核中,效率很高,但生成的二进制文件会变大,我个人猜想,这个函数之所以等级这么高,是因为我们应用层开发者写的代码就是用这个函数压进我们需要运行的队列中的。

上面说到如果队列中没有任务,则会使用 _dispatch_wakeup() 函数唤醒队列,让我们来看一下是怎么唤醒队列的:


_dispatch_wakeup

在上图中,我们要根据传入不同的队列来进行分析:

1.传入主队列,会进入到 _dispatch_queue_wakeup_main() 函数中,这里不加以分析。

2.传入全局队列,会进入到 全局队列的 dx_probe 函数中,也就是我上文说的 _dispatch_queue_wakeup_global 函数,等下会重点分析这个函数。

3.传入用户自定义的队列,首先用户自定义的任务是没有 dx_probe 函数的,所以会继续往下走,在这里可以看到,它会被压进它的目标队列,也就是全局队列中。然后进入我刚才说的 2 的传入全局队列模式中且这个队列接到了全局队列中。

重点分析一下 dispatch_queue_wakeup_global:



dispatch_queue_wakeup_global

这个函数前面是对传入的全局队列的检测,初始化和配置环境,我的讲解将从它发送了一个信号量开始。

这个函数里面发送了一个信号量,这是一种线程保活的方法,这里等我讲到这个信号量等待函数再来讲解。

继续往下走检测线程池的大小,如果线程池还有,则线程池减1。接下来就是使用 pthread 库创建一个线程,线程的入口是 _dispatch_worker_thread 。接下来的分析我们就进入了另一个线程了:

_dispatch_worker_thread

这是新线程的入口,前面主要是设置新线程的信号掩码,真正的任务调度在 _dispatch_worker_thread2 里面,而我们也可以看到,这个任务调度结束后,这个线程在等待一个信号量,而等待的信号量就是前面 dispatch_queue_wakeup_global 里面的信号量,为什么要这样做?这样做的原因是不要频繁开启新线程,如果有一个新线程完成所有任务了,这个线程就要结束了,但这里并不是这样,而是等待一个信号量,大约等待65秒,如果65秒内接收到新的信号量(表示有新的任务),这个线程就会去继续执行加进来的任务,而不是重新开启新线程,65秒后没接收到信号量,则退出这个线程,销毁这个线程。

接下来看一下任务怎么调度的:

_dispatch_worker_thread2

这个函数里面进行任务的调度,主要是两个函数,一个是 _dispatch_queue_concurrent_drain_one ,用来取出队列的一个内容,另一个是 _dispatch_continuation_pop 函数,用来对取出的内容进行处理(如果是任务,则执行任务)。首先来看一下 _dispatch_queue_concurrent_drain_one 函数:



_dispatch_queue_concurrent_drain_one

这个函数首先检测形参队列是不是空的,如果空的,就返回NULL。如果这个队列(其实是全局队列的一个)有内容,就分成两种情况:

1.这个队列只有一个内容,就返回这个内容;

2.这个队列不止只有一个内容,就调用 _dispatch_queue_wakeup_global 函数(其实就是创建一个新的线程,继续调度这个队列里面的内容);

这样,就可以从 _dispatch_queue_concurrent_drain_one 函数中获得了队列的一个内容,然后 _dispatch_continuation_pop 这个函数进行处理这个内容:

_dispatch_continuation_pop

这个函数首先检测传进来的内容是不是队列,如果是队列,就进入 _dispatch_queue_invoke 处理队列,如果不是,那这个形参就是任务封装的 dispatch_continuation_t 结构体,直接执行任务。

这里就不截图源码来说明 _dispatch_queue_invoke 函数了,我这里简单的说明一下它的逻辑,_dispatch_queue_invoke 函数首先会检测出这个队列是不是串行队列,如果是串行队列,就一个一个取出任务执行,如果是并发队列,就取出一个任务执行,然后将这个并发队列的其余部分压进 rootqueue 里面,这样就使并发队列的任务并发执行了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容