GCD
Grand Central Dispatch(GCD)是Apple开发的一个多核编程的解决方法。该方法在MacOSX10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue等技术的很高效和强大的技术。GCD能够帮助我们使用非常简洁高效的方法实现复杂繁琐的多线程编程。
下面的例子列举了简单的异步线程处理,在后台处理完耗时的程序之后,在主线程更新UI。
|
|
GCD的实现原理
简单来说,GCD的实现需要使用这些工具:
- 用于管理追加的Block的C语言实现的FIFO队列
- Atomic函数中实现的额用于排他控制的轻量级信号
- 用于管理线程的C语言层实现的一些容器
通常,应用程序中编写的线程管理应用的代码要在系统iOS和OS X的核心XNU内核级上实现。因此,无论编程人员如何努力编写管理线程的代码,在性能方面也不可能胜过XNU内核级所实现的GCD。
使用GCD要比使用pthreads和NSThread这些一般的多线程编程API更好。并且,如果使用GCD就不必编写为操作线程反复出现的类似的代码(这被称为固定源代码片段),而可以在线程中集中实现处理内容。我们尽量多使用GCD或者使用了Cocoa框架GCD的NSOperationQueue类等API。
用于实现Dispatch Queue而使用的软件组件。
组件名称 | 提供技术 |
---|---|
libdispatch | Dispatch Queue |
Libc(pthreads) | pthread_workqueue |
XNU内核 | workqueue |
编程人员所使用GCD的API全部包含在libdispatch库中的C语言函数。Dispatch Queue通过结构体和链表,被实现为FIFO队列。FIFO队列管理是通过dispatch_async函数所追加的Block。
Block并不是直接加入FIFO队列,而是先加入Dispatch Continuation这一==dispatch_continuation_t==类型结构体中,然后再加入FIFO队列。该Dispatch Continuation用于记忆Block所属的Dispatch Group和其他一些信息,相当于一般常说的执行上下文。
Dispatch Queue可通过==dispatch_set_target_queue==函数设定,可以设定执行该Dispatch Queue处理的Dispatch Queue为目标。该目标可像串珠子一样,设定多个连接在一起的Dispatch Queue。但是在连接串的最后必须设定为Main Dispatch Queue,或各种优先级的Global Dispatch Queue,或是准备用于Serial Dispatch Queue的各种优先级的Global Dispatch Queue。
Main Dispatch Queue在RunLoop 中执行Block。
Global Dispatch Queue有如下8中:
|
|
优先级中附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中。如Overcommit 这个名称所示,不管系统状态如何,都会强制生成线程的Dispatch Queue。
这8种Global Dispatch Queue各使用1个pthread_workqueue。GCD初始化时,使用pthread_workqueue_create_np函数生成pthread_workqueue。
pthread_workqueue包含在Libc提供的pthreads API中。其使用bsdthread_register和workq_open系统调用,在初始化XNU内核的workqueue之后获取workqueue信息。
XNU内核持有4中workqueue:
|
|
以上为4中执行优先级的workqueue。该执行优先级与Global Dispatch Queue的4种执行优先级相同。
下面看一下Dispatch Queue中执行Block的过程。当在Global Dispatch Queue中执行Block时,libdispatch从Global Dispatch Queue自身的FIFO队列中提出Dispatch Continuation,调用pthread_workqueue_additem_np函数。将该Global Dispatch Queue自身、符合其优先级的workqueue信息以及为执行Dispatch Continuation的回调函数等传递给参数。
该线程虽然与iOS和OS X中通常使用的线程大致相同,但是有一部分pthread API不能使用。详细信息科参考苹果的官方文档《并列编程指南》的“与POSIX线程的互换性”一节。
另外,因为workqueue生成的线程在实现用于workqueue的线程计划表中运行,所以与一般线程的上下文切换不同。这里也隐藏着使用GCD的原因。
Block执行结束后,进行通知Dispatch Group结束、释放Dispatch Continuation等处理,开始准备执行加入到Globar Dispatch Queue的下一个Block。
Dispatch Source
GCD中除了主要的Dispatch Queue外,还有不太引人注目的Dispatch Source。它是BSD系内核惯有功能kqueue的包装。
kqueue是XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU负荷非常小,尽量不占用资源。kqueue可以说是应用程序处理XNU内核中发生的各种事件的方法中最优秀的一种。
Dispatch Source可处理以下事件:
|
|
事件发生时,在指定的Dispatch Queue中可执行事件的处理。
下面我们使用DISPATCH_SOURCE_TYPE_READ,异步读取文件映像。
|
|
与上面代码非常相似的代码,使用在了Core Foundation框架的用于异步网络的API CFSocket中。因为Foundation框架的异步网络API是通过CFSocket实现的,所以可享受到仅使用Foundation框架的Dispatch Source(即GCD)带来的好处。
参考文档
- 《Objective-C 高级编程iOS 与OS X多线程和内存管理》
- 《Effective Objective-C 2.0》
- 《ConcurrencyProgrammingGuide》