GCD (Grand Central Dispatch),在系统级即iOS和OS X的核心XNU内核级上实现,所以开发者无论如何努力编写线程关系代码,
性能
都不可能胜过XNU内核级所实现的GCD。
Dispatch Queue实现
# Dispatch Quene实现的所需
- 用于管理追加的Block的C语言实现的FIFO队列。
- Atomic函数中实现的用于排他控制的轻量级信号。
- 用于管理线程的C语言实现的一些容器。
# 用于实现Dispatch Queue的几个软件组件框架
- 组件libdispatch提供Dispatch Quene技术。
- 组件Libc(pthreads)提供pthread_workquene技术。
- 组件XNU内核提供workquene技术。
# 执行上下文
Dispatch Quene通过结构体和链表,被实现为FIFO队列。
Block并不是直接加入FIFO队列,而是先加入Dispatch Continuation
这一dispatch_continuation_t类型
结构体中,然后再加入FIFO队列。该Dispatch Continuation用于记忆Block所属的Dispatch Group和其他一些信息,相当于一般常说的执行上下文
上一篇在将Global Dispatch Queue
的时候,我们介绍过8种类型,这8种Global Dispatch Quene各使用一个pthread_workquene。
GCD初始化时,使用pthread_workquene_creat_np函数生成"pthread_workquene"
pthread_workquene包含在Libc提供的pthreads API 中。
其使用bsdthread_register和workq_open系统调用,"在初始化XNU内核的workquene之后获取workquene信息"
XNU内核持有4种workquene:
WORKQUENE_HIGH_PRIOQUENE
WORKQUENE_Default_PRIOQUENE
WORKQUENE_Low_PRIOQUENE
WORKQUENE_BG_PRIOQUENE
优先级与Global Dispatch Quene的四种执行优先级相同。
Global Dispatch Queue --> Libc pthread_wordqueue --> XNU workqueue
# Dispatch Queue执行Block的过程
- 在Global Dispatch Queue 中执行Block时,libdispatch 从Global Dispatch Queue自身的FIFO队列取出
Dispatch Continuation
- 在Global Dispatch Queue 中执行Block时,libdispatch 从Global Dispatch Queue自身的FIFO队列取出
- 调用
pthread_workqueue_additem_np
函数将该Global Dispatch Queue 本身、符合其优先级的workqueue信息以及执行Dispatch Continuation的回调函数等传递给参数。
- 调用
- pthread_workqueue_additem_np函数使用
workq_kernreturn系统调用
,通知workqueue增加应当执行的项目。
根据该通知,XNU内核基于系统状态判断是否要生成线程。如果是Overcommit优先级
的Global Dispatch Queue ,workqueue则始终生成线程(该线程虽然与iOS和OS X中通常使用的线程大致相同,但是有一部分pthread API不能使用)。
因为workqueue生成的线程在实现用于workqueue的线程计划表中运行,他的上下文切换(shift context)
与普通的线程有很大的不同。这也是我们使用GCD的原因。
- pthread_workqueue_additem_np函数使用
- workqueue的线程 --> 执行pthread_workqueue函数 --> 该函数调用libdispatch的回调函数。在该回调函数中执行加入到Global Dispatch Queue中的下一个Block。
- Block执行结束后,进行通知Dispatch Group结束、释放Dispatch Continuation等处理,开始准备执行加入到Global Dispatch Queue中的下一个Block
Dispatch Source
GCD中除了主要的Dispatch Queue外,还有不太引人注目的Dispatch Source。它是BSD系内核惯有功能kqueue的包装
。
kqueue是XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU负荷非常小,尽量不占用资源。kqueue可以说是应用程序处理XNU内核中发生的各种事件的方法中最优秀的一种。
Dispatch Source可处理以下事件:
DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加
DISPATCH_SOURCE_TYPE_DATA_OR 变量OR
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_PROC 检测到与进程相关的事件
DISPATCH_SOURCE_TYPE_READ 可读取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL 接收信号
DISPATCH_SOURCE_TYPE_TIMER 定时器
DISPATCH_SOURCE_TYPE_VNODE 文件系统有变更
DISPATCH_SOURCE_TYPE_WRITE 可写入文件映像
事件发生时,在指定的Dispatch Queue中可执行事件的处理。
dispatch_queue_t queue = dispatc_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
* 基于READ事件作成Dispatch Source
*/
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0 , queuq);
/* 指定发生READ事件时执行的处理 */
dispatch_source_set_event_handler(source, ^{
// 处理结束,取消Dispatch Source
dispatch_source_cancel(source);
});
/* 指定取消Dispatch Source时的处理 */
dispatch_source_set_cancel_handler(source, ^{
/* 释放Dispatch Source(自身) */
dispatch_release(source);
});
/* 启动Dispatch Source */
dispatch_resume(source);
与上面代码非常相似的代码,使用在了Core Foundation框架的用于异步网络的API CFSocket
中。因为Foundation框架的异步网络API是通过CFSocket实现
的,所以可享受到仅使用Foundation框架的Dispatch Source(即GCD)带来的好处。
一旦将任务追加到Dispatch Queue中,就没有办法将任务取消,也没有办法在执行中取消任务。Dispatch Source是可以取消的,而且取消时的处理可以block的形式作为参数配置。在必须使用kqueue的情况下,推荐大家使用Dispatch Source,比较简单
网友评论