细说GCD(Grand Central Dispatch)如何用

来源:互联网 发布:怎么把mac文件拷到u盘 编辑:程序博客网 时间:2024/06/03 17:32

文中较详细介绍GCD队列,各种GCD使用方法,实例如何使用Dispatch Source监听系统底层对象,分析不同锁的性能对比,实例GCD死锁情况。文中的Demo在这里 https://github.com/ming1016/GCDDemo 对着文章试着来调demo体会更深哦,细细嚼消化好:)

GCD(Grand Central Dispatch) 介绍

GCD属于系统级的线程管理,在Dispatch queue中执行需要执行的任务性能非常的高。GCD这块已经开源,地址http://libdispatch.macosforge.org。GCD中的FIFO队列称为dispatch queue,用来保证先进来的任务先得到执行。

GCD概要

  • 和operation queue一样都是基于队列的并发编程API,他们通过集中管理大家协同使用的线程池。
  • 公开的5个不同队列:运行在主线程中的main queue,3个不同优先级的后台队列(High Priority Queue,Default Priority Queue,Low Priority Queue),以及一个优先级更低的后台队列Background Priority Queue(用于I/O)
  • 可创建自定义队列:串行或并列队列。自定义一般放在Default Priority Queue和Main Queue里。
  • 操作是在多线程上还是单线程主要是看队列的类型和执行方法,并行队列异步执行才能在多线程,并行队列同步执行就只会在主线程执行了

基本概念

  • 系统标准两个队列
<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//全局队列,一个并行的队列</span>dispatch_get_global_queue<span class="hljs-comment" style="color: rgb(136, 0, 0);">//主队列,主线程中的唯一队列,一个串行队列</span>dispatch_get_main_queue</code>
  • 自定义队列
<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//串行队列</span>dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.serialqueue"</span>, DISPATCH_QUEUE_SERIAL)<span class="hljs-comment" style="color: rgb(136, 0, 0);">//并行队列</span>dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.concurrentqueue"</span>, DISPATCH_QUEUE_CONCURRENT)</code>
  • 同步异步线程创建
<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//同步线程</span><span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(..., ^(block))<span class="hljs-comment" style="color: rgb(136, 0, 0);">//异步线程</span><span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(..., ^(block))</code>

队列(dispatch queue)

  • Serial:又叫private dispatch queues,同时只执行一个任务。Serial queue常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然各自是同步,但serial queue之间是并发执行。
  • Main dispatch queue:全局可用的serial queue,在应用程序主线程上执行任务。
  • Concurrent:又叫global dispatch queue,可以并发的执行多个任务,但执行完成顺序是随机的。系统提供四个全局并发队列,这四个队列有这对应的优先级,用户是不能够创建全局队列的,只能获取。
<code class="cpp"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dipatch_queue_t</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span>;<span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span> = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>);</code>
  • user create queue:创建自己定义的队列,可以用dispatch_queue_create函数,函数有两个参数,第一个自定义的队列名,第二个参数是队列类型,默认NULL或者DISPATCH_QUEUE_SERIAL的是串行,参数为DISPATCH_QUEUE_CONCURRENT为并行队列。
<code class="cpp"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span><span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span> = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>, DISPATCH_QUEUE_CONCURRENT);</code>
  • 自定义队列的优先级:可以通过dipatch_queue_attr_make_with_qos_class或dispatch_set_target_queue方法设置队列的优先级
<code class="cpp"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//dipatch_queue_attr_make_with_qos_class</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_attr_t</span> attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>);<span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span> = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.qosqueue"</span>, attr);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//dispatch_set_target_queue</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span> = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.settargetqueue"</span>,<span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0);">//需要设置优先级的queue</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0);">//参考优先级</span>dispatch_set_target_queue(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">queue</span>, referQueue); <span class="hljs-comment" style="color: rgb(136, 0, 0);">//设置queue和referQueue的优先级一样</span></code>
  • dispatch_set_target_queue:可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下
<code class="lisp">dispatch_queue_t serialQueue = dispatch_queue_create<span class="hljs-list">(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.serialqueue"</span>, DISPATCH_QUEUE_SERIAL)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_queue_t firstQueue = dispatch_queue_create<span class="hljs-list">(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.firstqueue"</span>, DISPATCH_QUEUE_SERIAL)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_queue_t secondQueue = dispatch_queue_create<span class="hljs-list">(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.secondqueue"</span>, DISPATCH_QUEUE_CONCURRENT)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_set_target_queue<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">firstQueue</span>, serialQueue)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_set_target_queue<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">secondQueue</span>, serialQueue)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_async<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">firstQueue</span>, ^{    NSLog<span class="hljs-list">(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"1"</span>)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>    [NSThread sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">3</span>.f]<span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>})</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_async<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">secondQueue</span>, ^{    NSLog<span class="hljs-list">(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"2"</span>)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>    [NSThread sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2</span>.f]<span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>})</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_async<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">secondQueue</span>, ^{    NSLog<span class="hljs-list">(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"3"</span>)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>    [NSThread sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>.f]<span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>})</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span></code>

队列类型

队列默认是串行的,如果设置改参数为NULL会按串行处理,只能执行一个单独的block,队列也可以是并行的,同一时间执行多个block

<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">id</span>)init;{     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> = [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">super</span> init];     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> != <span class="hljs-literal" style="color: rgb(0, 102, 102);">nil</span>) {          <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSString</span> *label = [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSString</span> stringWithFormat:<span class="hljs-string" style="color: rgb(0, 136, 0);">@"%@.isolation.%p"</span>, [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> class], <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>];          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.isolationQueue</span> = dispatch_queue_create([label UTF8String], <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>);          label = [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSString</span> stringWithFormat:<span class="hljs-string" style="color: rgb(0, 136, 0);">@"%@.work.%p"</span>, [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> class], <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>];          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.workQueue</span> = dispatch_queue_create([label UTF8String], <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>);     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>;}</code>

5种队列,主队列(main queue),四种通用调度队列,自己定制的队列。四种通用调度队列为

  • QOS_CLASS_USER_INTERACTIVE:user interactive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
  • QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
  • QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
  • QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。

示例:后台加载显示图片

<code class="swift"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">override</span> <span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">viewDidLoad</span><span class="hljs-params" style="color: rgb(102, 0, 102);">()</span></span> {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">super</span>.viewDidLoad()     dispatch_async(dispatch_get_global_queue(<span class="hljs-type">Int</span>(<span class="hljs-type">QOS_CLASS_USER_INITIATED</span>.value), <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 将工作从主线程转移到全局队列中,这是dispatch_async调用,异步提交保证调用线程会继续执行下去,这样viewDidLoad在主线程上能够更早完成,</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> overlayImage = <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.faceOverlayImageFromImage(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.image)          dispatch_async(dispatch_get_main_queue()) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 新图完成,把一个闭包加入主线程用来更新UIImageView,只有在主线程能操作UIKit。</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.fadeInNewImage(overlayImage) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 更新UI</span>          }     }}</code>

何时使用何种队列类型

  • 主队列(顺序):队列中有任务完成需要更新UI时,dispatch_after在这种类型中使用。
  • 并发队列:用来执行与UI无关的后台任务,dispatch_sync放在这里,方便等待任务完成进行后续处理或和dispatch barrier同步。dispatch groups放在这里也不错。
  • 自定义顺序队列:顺序执行后台任务并追踪它时。这样做同时只有一个任务在执行可以防止资源竞争。dipatch barriers解决读写锁问题的放在这里处理。dispatch groups也是放在这里。

可以使用下面的方法简化QoS等级参数的写法

<code class="swift"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> <span class="hljs-type">GlobalMainQueue</span>: dispatch_queue_t {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> dispatch_get_main_queue()}<span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> <span class="hljs-type">GlobalUserInteractiveQueue</span>: dispatch_queue_t {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> dispatch_get_global_queue(<span class="hljs-type">Int</span>(<span class="hljs-type">QOS_CLASS_USER_INTERACTIVE</span>.value), <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)}<span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> <span class="hljs-type">GlobalUserInitiatedQueue</span>: dispatch_queue_t {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> dispatch_get_global_queue(<span class="hljs-type">Int</span>(<span class="hljs-type">QOS_CLASS_USER_INITIATED</span>.value), <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)}<span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> <span class="hljs-type">GlobalUtilityQueue</span>: dispatch_queue_t {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> dispatch_get_global_queue(<span class="hljs-type">Int</span>(<span class="hljs-type">QOS_CLASS_UTILITY</span>.value), <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)}<span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> <span class="hljs-type">GlobalBackgroundQueue</span>: dispatch_queue_t {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> dispatch_get_global_queue(<span class="hljs-type">Int</span>(<span class="hljs-type">QOS_CLASS_BACKGROUND</span>.value), <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//使用起来就是这样,易读而且容易看出在使用哪个队列</span>dispatch_async(<span class="hljs-type">GlobalUserInitiatedQueue</span>) {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> overlayImage = <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.faceOverlayImageFromImage(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.image)     dispatch_async(<span class="hljs-type">GlobalMainQueue</span>) {          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.fadeInNewImage(overlayImage)     }}</code>

dispatch_once用法

dispatch_once_t要是全局或static变量,保证dispatch_once_t只有一份实例

<code class="objectivec">+ (<span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIColor</span> *)boringColor;{     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">static</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIColor</span> *color;     <span class="hljs-comment" style="color: rgb(136, 0, 0);">//只运行一次</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">static</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_once_t</span> onceToken;     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_once</span>(&onceToken, ^{          color = [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIColor</span> colorWithRed:<span class="hljs-number" style="color: rgb(0, 102, 102);">0.380</span>f green:<span class="hljs-number" style="color: rgb(0, 102, 102);">0.376</span>f blue:<span class="hljs-number" style="color: rgb(0, 102, 102);">0.376</span>f alpha:<span class="hljs-number" style="color: rgb(0, 102, 102);">1.000</span>f];     });     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> color;}</code>

dispatch_async

设计一个异步的API调用dispatch_async(),这个调用放在API的方法或函数中做。让API的使用者设置一个回调处理队列

<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)processImage:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIImage</span> *)image completionHandler:(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>(^)(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">BOOL</span> success))handler;{     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.isolationQueue</span>, ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>){          <span class="hljs-comment" style="color: rgb(136, 0, 0);">// do actual processing here</span>          <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.resultQueue</span>, ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>){               handler(<span class="hljs-literal" style="color: rgb(0, 102, 102);">YES</span>);          });     });}</code>

可以避免界面会被一些耗时的操作卡死,比如读取网络数据,大数据IO,还有大量数据的数据库读写,这时需要在另一个线程中处理,然后通知主线程更新界面,GCD使用起来比NSThread和NSOperation方法要简单方便。

<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//代码框架</span><span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{     <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 耗时的操作</span>     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_main_queue(), ^{          <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 更新界面</span>     });});<span class="hljs-comment" style="color: rgb(136, 0, 0);">//下载图片的示例</span><span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURL</span> * url = [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURL</span> URLWithString:<span class="hljs-string" style="color: rgb(0, 136, 0);">@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"</span>];     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSData</span> * data = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSData</span> alloc]initWithContentsOfURL:url];     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIImage</span> *image = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102);">UIImage</span> alloc]initWithData:data];     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (data != <span class="hljs-literal" style="color: rgb(0, 102, 102);">nil</span>) {          <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_main_queue(), ^{               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.imageView</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.image</span> = image;          });     }});</code>

dispatch_after延后执行

dispatch_after只是延时提交block,不是延时立刻执行。

<code class="cpp">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)foo{     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">double</span> delayInSeconds = <span class="hljs-number" style="color: rgb(0, 102, 102);">2.0</span>;     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_time_t</span> popTime = dispatch_time(DISPATCH_TIME_NOW, (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">int64_t</span>) (delayInSeconds * NSEC_PER_SEC));     dispatch_after(popTime, dispatch_get_main_queue(), ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>){          [self bar];     });}</code>

范例,实现一个推迟出现弹出框提示,比如说提示用户评价等功能。

<code class="swift"><span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">showOrHideNavPrompt</span><span class="hljs-params" style="color: rgb(102, 0, 102);">()</span></span> {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> delayInSeconds = <span class="hljs-number" style="color: rgb(0, 102, 102);">1.0</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> popTime = dispatch_time(<span class="hljs-type">DISPATCH_TIME_NOW</span>,          <span class="hljs-type">Int64</span>(delayInSeconds * <span class="hljs-type">Double</span>(<span class="hljs-type">NSEC_PER_SEC</span>))) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 在这里声明推迟的时间</span>     dispatch_after(popTime, <span class="hljs-type">GlobalMainQueue</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 等待delayInSeconds将闭包异步到主队列</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span> = <span class="hljs-type">PhotoManager</span>.sharedManager.photos.<span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span> > <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span> {               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.navigationItem.prompt = <span class="hljs-literal" style="color: rgb(0, 102, 102);">nil</span>          } <span class="hljs-keyword" style="color: rgb(0, 0, 136);">else</span> {               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.navigationItem.prompt = <span class="hljs-string" style="color: rgb(0, 136, 0);">"Add photos with faces to Googlyify them!"</span>          }     }}</code>

例子中的dispatch time的参数,可以先看看函数原型

<code class="cpp"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_time_t</span> dispatch_time ( <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_time_t</span> when, <span class="hljs-keyword" style="color: rgb(0, 0, 136);">int64_t</span> delta );</code>

第一个参数为DISPATCH_TIME_NOW表示当前。第二个参数的delta表示纳秒,一秒对应的纳秒为1000000000,系统提供了一些宏来简化

<code class="cpp"> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68);">#<span class="hljs-keyword">define</span> NSEC_PER_SEC <span class="hljs-number">1000000000u</span>ll <span class="hljs-comment">//每秒有多少纳秒</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68);">#<span class="hljs-keyword">define</span> USEC_PER_SEC <span class="hljs-number">1000000u</span>ll    <span class="hljs-comment">//每秒有多少毫秒</span></span> <span class="hljs-preprocessor" style="color: rgb(68, 68, 68);">#<span class="hljs-keyword">define</span> NSEC_PER_USEC <span class="hljs-number">1000u</span>ll      <span class="hljs-comment">//每毫秒有多少纳秒</span></span></code>

这样如果要表示一秒就可以这样写

<code class="lisp">dispatch_time<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">DISPATCH_TIME_NOW</span>, <span class="hljs-number" style="color: rgb(0, 102, 102);">1</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">* NSEC_PER_SEC);dispatch_time(DISPATCH_TIME_NOW, 1000 *</span> USEC_PER_SEC)</span><span class="hljs-comment" style="color: rgb(136, 0, 0);">;</span>dispatch_time<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">DISPATCH_TIME_NOW</span>, USEC_PER_SEC <span class="hljs-variable" style="color: rgb(102, 0, 102);">* NSEC_PER_USEC);</span></span></code>

dispatch_barrier_async使用Barrier Task方法Dispatch Barrier解决多线程并发读写同一个资源发生死锁

Dispatch Barrier确保提交的闭包是指定队列中在特定时段唯一在执行的一个。在所有先于Dispatch Barrier的任务都完成的情况下这个闭包才开始执行。轮到这个闭包时barrier会执行这个闭包并且确保队列在此过程不会执行其它任务。闭包完成后队列恢复。需要注意dispatch_barrier_async只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和dispatch_sync一样

<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//创建队列</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.isolationQueue</span> = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//改变setter</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)setCount:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSUInteger</span>)count forKey:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSString</span> *)key{     key = [key <span class="hljs-keyword" style="color: rgb(0, 0, 136);">copy</span>];     <span class="hljs-comment" style="color: rgb(136, 0, 0);">//确保所有barrier都是async异步的</span>     dispatch_barrier_async(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.isolationQueue</span>, ^(){          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (count == <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>) {               [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.counts</span> removeObjectForKey:key];          } <span class="hljs-keyword" style="color: rgb(0, 0, 136);">else</span> {               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.counts</span>[key] = @(count);          }     });}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchBarrierAsyncDemo {    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//防止文件读写冲突,可以创建一个串行队列,操作都在这个队列中进行,没有更新数据读用并行,写用串行。</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> dataQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.dataqueue"</span>, DISPATCH_QUEUE_CONCURRENT);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dataQueue, ^{        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"read data 1"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dataQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"read data 2"</span>);    });    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//等待前面的都完成,在执行barrier后面的</span>    dispatch_barrier_async(dataQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"write data 1"</span>);        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>];    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dataQueue, ^{        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">1.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"read data 3"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dataQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"read data 4"</span>);    });}</code>

swift示例

<code class="swift"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//使用dispatch_queue_create初始化一个并发队列。第一个参数遵循反向DNS命名习惯,方便描述,第二个参数是指出是并发还是顺序。</span>private <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> concurrentPhotoQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.raywenderlich.GooglyPuff.photoQueue"</span>, <span class="hljs-type">DISPATCH_QUEUE_CONCURRENT</span>)<span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">addPhoto</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(photo: Photo)</span></span> {     dispatch_barrier_async(concurrentPhotoQueue) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 将写操作加入到自定义的队列。开始执行时这个就是队列中唯一的一个在执行的任务。</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>._photos.append(photo) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// barrier能够保障不会和其他任务同时进行。</span>          dispatch_async(<span class="hljs-type">GlobalMainQueue</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 涉及到UI所以这个通知应该在主线程中,所以分派另一个异步任务到主队列中。</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>.postContentAddedNotification()          }     }}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//上面是解决了写可能发生死锁,下面是使用dispatch_sync解决读时可能会发生的死锁。</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> photos: [<span class="hljs-type">Photo</span>] {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> photosCopy: [<span class="hljs-type">Photo</span>]!     dispatch_sync(concurrentPhotoQueue) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 同步调度到concurrentPhotoQueue队列执行读操作</span>          photosCopy = <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>._photos <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 保存</span>     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span> photosCopy}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//这样读写问题都解决了。</span></code>

都用异步处理避免死锁,异步的缺点在于调试不方便,但是比起同步容易产生死锁这个副作用还算小的。

dispatch_apply进行快速迭代

类似for循环,但是在并发队列的情况下dispatch_apply会并发执行block任务。

<code class="cpp"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">size_t</span> y = <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>; y < height; ++y) {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">size_t</span> x = <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>; x < width; ++x) {          <span class="hljs-comment" style="color: rgb(136, 0, 0);">// Do something with x and y here</span>     }}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//因为可以并行执行,所以使用dispatch_apply可以运行的更快</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchApplyDemo {    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>, DISPATCH_QUEUE_CONCURRENT);    dispatch_apply(<span class="hljs-number" style="color: rgb(0, 102, 102);">10</span>, concurrentQueue, ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">size_t</span> i) {        NSLog(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"%zu"</span>,i);    });    NSLog(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"The end"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0);">//这里有个需要注意的是,dispatch_apply这个是会阻塞主线程的。这个log打印会在dispatch_apply都结束后才开始执行</span>}</code>

dispatch_apply能避免线程爆炸,因为GCD会管理并发

<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dealWiththreadWithMaybeExplode:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">BOOL</span>)explode {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>,DISPATCH_QUEUE_CONCURRENT);    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (explode) {        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//有问题的情况,可能会死锁</span>        <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>; i < <span class="hljs-number" style="color: rgb(0, 102, 102);">999</span> ; i++) {            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(concurrentQueue, ^{                <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"wrong %d"</span>,i);                <span class="hljs-comment" style="color: rgb(136, 0, 0);">//do something hard</span>            });        }    } <span class="hljs-keyword" style="color: rgb(0, 0, 136);">else</span> {        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//会优化很多,能够利用GCD管理</span>        dispatch_apply(<span class="hljs-number" style="color: rgb(0, 102, 102);">999</span>, concurrentQueue, ^(size_t i){            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"correct %zu"</span>,i);            <span class="hljs-comment" style="color: rgb(136, 0, 0);">//do something hard</span>        });    }}</code>

示例:

<code class="swift"><span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">downloadPhotosWithCompletion</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(completion: BatchPhotoDownloadingCompletionClosure?)</span></span> {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> storedError: <span class="hljs-type">NSError</span>!     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> downloadGroup = dispatch_group_create()     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> addresses = [<span class="hljs-type">OverlyAttachedGirlfriendURLString</span>,          <span class="hljs-type">SuccessKidURLString</span>,          <span class="hljs-type">LotsOfFacesURLString</span>]     dispatch_apply(<span class="hljs-type">UInt</span>(addresses.<span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span>), <span class="hljs-type">GlobalUserInitiatedQueue</span>) {          i <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> index = <span class="hljs-type">Int</span>(i)          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> address = addresses[index]          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> url = <span class="hljs-type">NSURL</span>(string: address)          dispatch_group_enter(downloadGroup)          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> photo = <span class="hljs-type">DownloadPhoto</span>(url: url!) {               image, error <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> error = error {                    storedError = error               }               dispatch_group_leave(downloadGroup)          }          <span class="hljs-type">PhotoManager</span>.sharedManager.addPhoto(photo)     }     dispatch_group_notify(downloadGroup, <span class="hljs-type">GlobalMainQueue</span>) {          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> completion = completion {               completion(error: storedError)          }     }}</code>

Block组合Dispatch_groups

dispatch groups是专门用来监视多个异步任务。dispatch_group_t实例用来追踪不同队列中的不同任务。

当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。

第一种使用dispatch_group_wait的swift的例子:

<code class="swift"><span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">downloadPhotosWithCompletion</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(completion: BatchPhotoDownloadingCompletionClosure?)</span></span> {     dispatch_async(<span class="hljs-type">GlobalUserInitiatedQueue</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 因为dispatch_group_wait会租塞当前进程,所以要使用dispatch_async将整个方法要放到后台队列才能够保证主线程不被阻塞</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> storedError: <span class="hljs-type">NSError</span>!          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> downloadGroup = dispatch_group_create() <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 创建一个dispatch group</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> address <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span> [<span class="hljs-type">OverlyAttachedGirlfriendURLString</span>,               <span class="hljs-type">SuccessKidURLString</span>,               <span class="hljs-type">LotsOfFacesURLString</span>]          {               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> url = <span class="hljs-type">NSURL</span>(string: address)               dispatch_group_enter(downloadGroup) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// dispatch_group_enter是通知dispatch group任务开始了,dispatch_group_enter和dispatch_group_leave是成对调用,不然程序就崩溃了。</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> photo = <span class="hljs-type">DownloadPhoto</span>(url: url!) {                    image, error <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span>                    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> error = error {                         storedError = error                    }                    dispatch_group_leave(downloadGroup) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 保持和dispatch_group_enter配对。通知任务已经完成</span>               }               <span class="hljs-type">PhotoManager</span>.sharedManager.addPhoto(photo)          }          dispatch_group_wait(downloadGroup, <span class="hljs-type">DISPATCH_TIME_FOREVER</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// dispatch_group_wait等待所有任务都完成直到超时。如果任务完成前就超时了,函数会返回一个非零值,可以通过返回值判断是否超时。也可以用DISPATCH_TIME_FOREVER表示一直等。</span>          dispatch_async(<span class="hljs-type">GlobalMainQueue</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 这里可以保证所有图片任务都完成,然后在main queue里加入完成后要处理的闭包,会在main queue里执行。</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> completion = completion { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 执行闭包内容</span>                    completion(error: storedError)               }          }     }}</code>

oc例子

<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchGroupWaitDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>,DISPATCH_QUEUE_CONCURRENT);    dispatch_group_t group = dispatch_group_create();    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//在group中添加队列的block</span>    dispatch_group_async(group, concurrentQueue, ^{        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    });    dispatch_group_async(group, concurrentQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);    });    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"go on"</span>);}</code>

第二种使用dispatch_group_notify的swift的例子:

<code class="swift"><span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">downloadPhotosWithCompletion</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(completion: BatchPhotoDownloadingCompletionClosure?)</span></span> {     <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 不用加dispatch_async,因为没有阻塞主进程</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> storedError: <span class="hljs-type">NSError</span>!     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> downloadGroup = dispatch_group_create()     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> address <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span> [<span class="hljs-type">OverlyAttachedGirlfriendURLString</span>,          <span class="hljs-type">SuccessKidURLString</span>,          <span class="hljs-type">LotsOfFacesURLString</span>]     {          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> url = <span class="hljs-type">NSURL</span>(string: address)          dispatch_group_enter(downloadGroup)          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> photo = <span class="hljs-type">DownloadPhoto</span>(url: url!) {               image, error <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> error = error {                    storedError = error               }               dispatch_group_leave(downloadGroup)          }          <span class="hljs-type">PhotoManager</span>.sharedManager.addPhoto(photo)     }     dispatch_group_notify(downloadGroup, <span class="hljs-type">GlobalMainQueue</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// dispatch_group_notify和dispatch_group_wait的区别就是是异步执行闭包的,当dispatch groups中没有剩余的任务时闭包才执行。这里是指明在主队列中执行。</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> completion = completion {               completion(error: storedError)          }     }}</code>

oc例子

<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//dispatch_group_notify</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchGroupNotifyDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>,DISPATCH_QUEUE_CONCURRENT);    dispatch_group_t group = dispatch_group_create();    dispatch_group_async(group, concurrentQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    });    dispatch_group_async(group, concurrentQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);    });    dispatch_group_notify(group, dispatch_get_main_queue(), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"end"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"can continue"</span>);}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//dispatch_group_wait</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchGroupWaitDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>,DISPATCH_QUEUE_CONCURRENT);    dispatch_group_t group = dispatch_group_create();    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//在group中添加队列的block</span>    dispatch_group_async(group, concurrentQueue, ^{        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    });    dispatch_group_async(group, concurrentQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);    });    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"can continue"</span>);}</code>

如何对现有API使用dispatch_group_t

<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//给Core Data的-performBlock:添加groups。组合完成任务后使用dispatch_group_notify来运行一个block即可。</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)withGroup:(dispatch_group_t)group performBlock:(dispatch_block_t)block{     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (group == <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>) {          [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> performBlock:block];     } <span class="hljs-keyword" style="color: rgb(0, 0, 136);">else</span> {          dispatch_group_enter(group);          [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> performBlock:^(){               block();               dispatch_group_leave(group);          }];     }}<span class="hljs-comment" style="color: rgb(136, 0, 0);">//NSURLConnection也可以这样做</span>+ (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)withGroup:(dispatch_group_t)group     sendAsynchronousRequest:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURLRequest</span> *)request     queue:(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSOperationQueue</span> *)queue     completionHandler:(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span> (^)(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURLResponse</span>*, <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSData</span>*, <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSError</span>*))handler{     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (group == <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>) {          [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> sendAsynchronousRequest:request               queue:queue               completionHandler:handler];     } <span class="hljs-keyword" style="color: rgb(0, 0, 136);">else</span> {          dispatch_group_enter(group);          [<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> sendAsynchronousRequest:request                    queue:queue                    completionHandler:^(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURLResponse</span> *response, <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSData</span> *data, <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSError</span> *error){               handler(response, data, error);               dispatch_group_leave(group);          }];     }}</code>

注意事项

  • dispatch_group_async等价于dispatch_group_enter() 和 dispatch_group_leave()的组合。
  • dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
  • dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的

Dispatch Block

队列执行任务都是block的方式,

  • 创建block
<code class="cpp">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)createDispatchBlock {    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//normal way</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.concurrentqueue"</span>,DISPATCH_QUEUE_CONCURRENT);    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_block_t</span> block = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        NSLog(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"run block"</span>);    });    dispatch_async(concurrentQueue, block);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//QOS way</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_block_t</span> qosBlock = dispatch_block_create_with_qos_class(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, QOS_CLASS_USER_INITIATED, -<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>, ^{        NSLog(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"run qos block"</span>);    });    dispatch_async(concurrentQueue, qosBlock);}</code>
  • dispatch_block_wait:可以根据dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVER会一直等待block结束
<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchBlockWaitDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> serialQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.serialqueue"</span>, DISPATCH_QUEUE_SERIAL);    dispatch_block_t block = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"star"</span>);        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">5.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"end"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(serialQueue, block);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成</span>    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"ok, now can go on"</span>);}</code>
  • dispatch_block_notify:可以监视指定dispatch block结束,然后再加入一个block到队列中。三个参数分别为,第一个是需要监视的block,第二个参数是需要提交执行的队列,第三个是待加入到队列中的block
<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchBlockNotifyDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> serialQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.serialqueue"</span>, DISPATCH_QUEUE_SERIAL);    dispatch_block_t firstBlock = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"first block start"</span>);        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"first block end"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(serialQueue, firstBlock);    dispatch_block_t secondBlock = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"second block run"</span>);    });    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//first block执行完才在serial queue中执行second block</span>    dispatch_block_notify(firstBlock, serialQueue, secondBlock);}</code>
  • dispatch_block_cancel:iOS8后GCD支持对dispatch block的取消
<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchBlockCancelDemo {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> serialQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.serialqueue"</span>, DISPATCH_QUEUE_SERIAL);    dispatch_block_t firstBlock = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"first block start"</span>);        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">2.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"first block end"</span>);    });    dispatch_block_t secondBlock = dispatch_block_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"second block run"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(serialQueue, firstBlock);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(serialQueue, secondBlock);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//取消secondBlock</span>    dispatch_block_cancel(secondBlock);}</code>

使用dispatch block object(调度块)在任务执行前进行取消

dispatch block object可以为队列中的对象设置
示例,下载图片中途进行取消

<code class="swift"><span class="hljs-func"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">func</span> <span class="hljs-title">downloadPhotosWithCompletion</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(completion: BatchPhotoDownloadingCompletionClosure?)</span></span> {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> storedError: <span class="hljs-type">NSError</span>!     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> downloadGroup = dispatch_group_create()     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> addresses = [<span class="hljs-type">OverlyAttachedGirlfriendURLString</span>,          <span class="hljs-type">SuccessKidURLString</span>,          <span class="hljs-type">LotsOfFacesURLString</span>]     addresses += addresses + addresses <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 扩展address数组,复制3份</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">var</span> blocks: [dispatch_block_t] = [] <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 一个保存block的数组</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> i <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span> <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span> ..< addresses.<span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span> {          dispatch_group_enter(downloadGroup)          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> block = dispatch_block_create(<span class="hljs-type">DISPATCH_BLOCK_INHERIT_QOS_CLASS</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 创建一个block,block的标志是DISPATCH_BLOCK_INHERIT_QOS_CLASS</span>               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> index = <span class="hljs-type">Int</span>(i)               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> address = addresses[index]               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> url = <span class="hljs-type">NSURL</span>(string: address)               <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> photo = <span class="hljs-type">DownloadPhoto</span>(url: url!) {                    image, error <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span>                    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> error = error {                         storedError = error                    }                    dispatch_group_leave(downloadGroup)               }               <span class="hljs-type">PhotoManager</span>.sharedManager.addPhoto(photo)          }          blocks.append(block)          dispatch_async(<span class="hljs-type">GlobalMainQueue</span>, block) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 把这个block放到GlobalMainQueue上异步调用。因为全局队列是一个顺序队列所以方便取消对象block,同时可以保证下载任务在downloadPhotosWithCompletion返回后才开始执行。</span>     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">for</span> block <span class="hljs-keyword" style="color: rgb(0, 0, 136);">in</span> blocks[<span class="hljs-number" style="color: rgb(0, 102, 102);">3</span> ..< blocks.<span class="hljs-built_in" style="color: rgb(102, 0, 102);">count</span>] {          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> cancel = arc4random_uniform(<span class="hljs-number" style="color: rgb(0, 102, 102);">2</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 随机返回一个整数,会返回0或1</span>          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> cancel == <span class="hljs-number" style="color: rgb(0, 102, 102);">1</span> {               dispatch_block_cancel(block) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 如果是1就取消block,这个只能发生在block还在队列中并没有开始的情况下。因为把block已经放到了GlobalMainQueue中,所以这个地方会先执行,执行完了才会执行block。</span>               dispatch_group_leave(downloadGroup) <span class="hljs-comment" style="color: rgb(136, 0, 0);">// 因为已经dispatch_group_enter了,所以取消时也要将其都leave掉。</span>          }     }     dispatch_group_notify(downloadGroup, <span class="hljs-type">GlobalMainQueue</span>) {          <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">let</span> completion = completion {               completion(error: storedError)          }     }}</code>

Dispatch IO 文件操作

dispatch io读取文件的方式类似于下面的方式,多个线程去读取文件的切片数据,对于大的数据文件这样会比单线程要快很多。

<code class="objectivec"><span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(queue,^{<span class="hljs-comment" style="color: rgb(136, 0, 0);">/*read 0-99 bytes*/</span>});<span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(queue,^{<span class="hljs-comment" style="color: rgb(136, 0, 0);">/*read 100-199 bytes*/</span>});<span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(queue,^{<span class="hljs-comment" style="color: rgb(136, 0, 0);">/*read 200-299 bytes*/</span>});</code>
  • dispatch_io_create:创建dispatch io
  • dispatch_io_set_low_water:指定切割文件大小
  • dispatch_io_read:读取切割的文件然后合并。

苹果系统日志API里用到了这个技术,可以在这里查看:https://github.com/Apple-FOSS-Mirror/Libc/blob/2ca2ae74647714acfc18674c3114b1a5d3325d7d/gen/asl.c

<code class="cpp">pipe_q = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"PipeQ"</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//创建</span>pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">int</span> err){    close(fd);});*out_fd = fdpair[<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>];<span class="hljs-comment" style="color: rgb(136, 0, 0);">//设置切割大小</span>dispatch_io_set_low_water(pipe_channel, SIZE_MAX);dispatch_io_read(pipe_channel, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, SIZE_MAX, pipe_q, ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">bool</span> done, <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_data_t</span> pipedata, <span class="hljs-keyword" style="color: rgb(0, 0, 136);">int</span> err){    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (err == <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)    {        <span class="hljs-keyword" style="color: rgb(0, 0, 136);">size_t</span> len = dispatch_data_get_size(pipedata);        <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (len > <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>)        {            <span class="hljs-comment" style="color: rgb(136, 0, 0);">//对每次切块数据的处理</span>            <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">char</span> *bytes = <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>;            <span class="hljs-keyword" style="color: rgb(0, 0, 136);">char</span> *encoded;            <span class="hljs-keyword" style="color: rgb(0, 0, 136);">uint32_t</span> eval;            <span class="hljs-keyword" style="color: rgb(0, 0, 136);">dispatch_data_t</span> md = dispatch_data_create_map(pipedata, (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span> **)&bytes, &len);            encoded = asl_core_encode_buffer(bytes, len);            asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded);            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">free</span>(encoded);            eval = _asl_evaluate_send(<span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>, (aslmsg)aux, -<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>);            _asl_send_message(<span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>, eval, aux, <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>);            asl_msg_release(aux);            dispatch_release(md);        }    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (done)    {        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//semaphore +1使得不需要再等待继续执行下去。</span>        dispatch_semaphore_signal(sem);        dispatch_release(pipe_channel);        dispatch_release(pipe_q);    }});</code>

Dispatch Source 用GCD监视进程

Dispatch Source用于监听系统的底层对象,比如文件描述符,Mach端口,信号量等。主要处理的事件如下表

方法说明DISPATCH_SOURCE_TYPE_DATA_ADD数据增加DISPATCH_SOURCE_TYPE_DATA_OR数据ORDISPATCH_SOURCE_TYPE_MACH_SENDMach端口发送DISPATCH_SOURCE_TYPE_MACH_RECVMach端口接收DISPATCH_SOURCE_TYPE_MEMORYPRESSURE内存情况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_source_create:创建dispatch source,创建后会处于挂起状态进行事件接收,需要设置事件处理handler进行事件处理。
  • dispatch_source_set_event_handler:设置事件处理handler
  • dispatch_source_set_cancel_handler:事件取消handler,就是在dispatch source释放前做些清理的事。
  • dispatch_source_cancel:关闭dispatch source,设置的事件处理handler不会被执行,已经执行的事件handler不会取消。
<code class="objectivec"><span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSRunningApplication</span> *mail = [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSRunningApplication</span> runningApplicationsWithBundleIdentifier:<span class="hljs-string" style="color: rgb(0, 136, 0);">@"com.apple.mail"</span>];<span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (mail == <span class="hljs-literal" style="color: rgb(0, 102, 102);">nil</span>) {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span>;}pid_t <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> pid = mail<span class="hljs-variable" style="color: rgb(102, 0, 102);">.processIdentifier</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.source</span> = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, DISPATCH_TARGET_QUEUE_DEFAULT);dispatch_source_set_event_handler(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.source</span>, ^(){     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"Mail quit."</span>);});<span class="hljs-comment" style="color: rgb(136, 0, 0);">//在事件源传到你的事件处理前需要调用dispatch_resume()这个方法</span>dispatch_resume(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.source</span>);</code>

监视文件夹内文件变化

<code class="objectivec"><span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSURL</span> *directoryURL; <span class="hljs-comment" style="color: rgb(136, 0, 0);">// assume this is set to a directory</span><span class="hljs-keyword" style="color: rgb(0, 0, 136);">int</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> fd = open([[directoryURL path] fileSystemRepresentation], O_EVTONLY);<span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (fd < <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>) {     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">char</span> buffer[<span class="hljs-number" style="color: rgb(0, 102, 102);">80</span>];     strerror_r(errno, buffer, <span class="hljs-keyword" style="color: rgb(0, 0, 136);">sizeof</span>(buffer));     <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"Unable to open \"%@\": %s (%d)"</span>, [directoryURL path], buffer, errno);     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">return</span>;}dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE, DISPATCH_TARGET_QUEUE_DEFAULT);dispatch_source_set_event_handler(source, ^(){     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">long</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> data = dispatch_source_get_data(source);     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (data & DISPATCH_VNODE_WRITE) {          <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"The directory changed."</span>);     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136);">if</span> (data & DISPATCH_VNODE_DELETE) {          <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"The directory has been deleted."</span>);     }});dispatch_source_set_cancel_handler(source, ^(){     close(fd);});<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.source</span> = source;dispatch_resume(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102);">.source</span>);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//还要注意需要用DISPATCH_VNODE_DELETE 去检查监视的文件或文件夹是否被删除,如果删除了就停止监听</span></code>

NSTimer在主线程的runloop里会在runloop切换其它模式时停止,这时就需要手动在子线程开启一个模式为NSRunLoopCommonModes的runloop,如果不想开启一个新的runloop可以用不跟runloop关联的dispatch source timer,如下。

<code class="bash">dispatch_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>_t <span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span> = dispatch_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>_create(DISPATCH_SOURCE_TYPE_TIMER,<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, DISPATCH_TARGET_QUEUE_DEFAULT);dispatch_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">set</span>_event_handler(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>, ^(){     NSLog(@<span class="hljs-string" style="color: rgb(0, 136, 0);">"Time flies."</span>);});dispatch_time_t startdispatch_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>_<span class="hljs-built_in" style="color: rgb(102, 0, 102);">set</span>_timer(<span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>, DISPATCH_TIME_NOW, <span class="hljs-number" style="color: rgb(0, 102, 102);">5</span>ull * NSEC_PER_SEC,<span class="hljs-number" style="color: rgb(0, 102, 102);">100</span>ull * NSEC_PER_MSEC);self.source = <span class="hljs-built_in" style="color: rgb(102, 0, 102);">source</span>;dispatch_resume(self.source);</code>

Dispatch Semaphore和的介绍

另外一种保证同步的方法。使用dispatch_semaphore_signal加1dispatch_semaphore_wait减1,为0时等待的设置方式来达到线程同步的目的和同步锁一样能够解决资源抢占的问题。

<code class="objectivec"><span class="hljs-comment" style="color: rgb(136, 0, 0);">//dispatch semaphore</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)dispatchSemaphoreDemo {    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//创建semaphore</span>    dispatch_semaphore_t semaphore = dispatch_semaphore_create(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"start"</span>);        [<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSThread</span> sleepForTimeInterval:<span class="hljs-number" style="color: rgb(0, 102, 102);">1.</span>f];        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"semaphore +1"</span>);        dispatch_semaphore_signal(semaphore); <span class="hljs-comment" style="color: rgb(136, 0, 0);">//+1 semaphore</span>    });    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"continue"</span>);}</code>

这里简单介绍下iOS中常用的各种锁和他们的性能。

  • NSRecursiveLock:递归锁,可以在一个线程中反复获取锁不会造成死锁,这个过程会记录获取锁和释放锁的次数来达到何时释放的作用。
  • NSDistributedLock:分布锁,基于文件方式的锁机制,可以跨进程访问。
  • NSConditionLock:条件锁,用户定义条件,确保一个线程可以获取满足一定条件的锁。因为线程间竞争会涉及到条件锁检测,系统调用上下切换频繁导致耗时是几个锁里最长的。
  • OSSpinLock:自旋锁,不进入内核,减少上下文切换,性能最高,但抢占多时会占用较多cpu,好点多,这时使用pthread_mutex较好。
  • pthread_mutex_t:同步锁基于C语言,底层api性能高,使用方法和其它的类似。
  • @synchronized:更加简单。

dispatch_suspend和dispatch_resume挂起和恢复队列

dispatch_suspend这里挂起不会暂停正在执行的block,只是能够暂停还没执行的block。

dispatch_set_context和dispatch_get_context

GCD深入操作

  • 缓冲区:dispatch_data_t基于零碎的内存区域,使用dispatch_data_apply来遍历,还可以用dispatch_data_create_subrange来创建一个不做任何拷贝的子区域
  • I/O调度:使用GCD提供的dispatch_io_read,dispatch_io_write和dispatch_io_close
  • 测试:使用dispatch_benchmark小工具
  • 原子操作: libkern/OSAtomic.h里可以查看那些函数,用于底层多线程编程。

GCD死锁

当前串行队列里面同步执行当前串行队列就会死锁,解决的方法就是将同步的串行队列放到另外一个线程就能够解决。

<code class="objectivec">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)deadLockCase1 {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//主队列的同步线程,按照FIFO的原则(先入先出),2排在3后面会等3执行完,但因为同步线程,3又要等2执行完,相互等待成为死锁。</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(dispatch_get_main_queue(), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"3"</span>);}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)deadLockCase2 {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//3会等2,因为2在全局并行队列里,不需要等待3,这样2执行完回到主队列,3就开始执行</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"3"</span>);}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)deadLockCase3 {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_queue_t</span> serialQueue = dispatch_queue_create(<span class="hljs-string" style="color: rgb(0, 136, 0);">"com.starming.gcddemo.serialqueue"</span>, DISPATCH_QUEUE_SERIAL);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(serialQueue, ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//串行队列里面同步一个串行队列就会死锁</span>        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(serialQueue, ^{            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"3"</span>);        });        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"4"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"5"</span>);}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)deadLockCase4 {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_global_queue(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//将同步的串行队列放到另外一个线程就能够解决</span>        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(dispatch_get_main_queue(), ^{            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"3"</span>);        });        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"4"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"5"</span>);}- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)deadLockCase5 {    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_async</span>(dispatch_get_global_queue(<span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102);">0</span>), ^{        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"1"</span>);        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//回到主线程发现死循环后面就没法执行了</span>        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">dispatch_sync</span>(dispatch_get_main_queue(), ^{            <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"2"</span>);        });        <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"3"</span>);    });    <span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSLog</span>(<span class="hljs-string" style="color: rgb(0, 136, 0);">@"4"</span>);    <span class="hljs-comment" style="color: rgb(136, 0, 0);">//死循环</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136);">while</span> (<span class="hljs-number" style="color: rgb(0, 102, 102);">1</span>) {        <span class="hljs-comment" style="color: rgb(136, 0, 0);">//</span>    }}</code>

GCD实际使用

FMDB如何使用dispatch_queue_set_specific和dispatch_get_specific来防止死锁

作用类似objc_setAssociatedObject跟objc_getAssociatedObject

<code class="objectivec"><span class="hljs-keyword" style="color: rgb(0, 0, 136);">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span> * <span class="hljs-keyword" style="color: rgb(0, 0, 136);">const</span> kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;<span class="hljs-comment" style="color: rgb(136, 0, 0);">//创建串行队列,所有数据库的操作都在这个队列里</span>_queue = dispatch_queue_create([[<span class="hljs-built_in" style="color: rgb(102, 0, 102);">NSString</span> stringWithFormat:<span class="hljs-string" style="color: rgb(0, 136, 0);">@"fmdb.%@"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>] UTF8String], <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//标记队列</span>dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge <span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span> *)<span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102);">NULL</span>);<span class="hljs-comment" style="color: rgb(136, 0, 0);">//检查是否是同一个队列来避免死锁的方法</span>- (<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span>)inDatabase:(<span class="hljs-keyword" style="color: rgb(0, 0, 136);">void</span> (^)(FMDatabase *db))block {    FMDatabaseQueue *currentSyncQueue = (__bridge <span class="hljs-keyword" style="color: rgb(0, 0, 136);">id</span>)dispatch_get_specific(kDispatchQueueSpecificKey);    assert(currentSyncQueue != <span class="hljs-keyword" style="color: rgb(0, 0, 136);">self</span> && <span class="hljs-string" style="color: rgb(0, 136, 0);">"inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"</span>);}</code>

iOS系统版本新特性

iOS8

iOS8新加了一个功能叫Quality of Service(QoS),里面提供了一下几个更容易理解的枚举名来使用user interactive,user initiated,utility和background。下面的表做了对比

Global queueCorresponding QoS class说明Main threadNSQualityOfServiceUserInteractiveUI相关,交互等DISPATCH_QUEUE_PRIORITY_HIGHNSQualityOfServiceUserInitiated用户发起需要马上得到结果进行后续任务DISPATCH_QUEUE_PRIORITY_DEFAULTNSQualityOfServiceDefault默认的不应该使用这个设置任务DISPATCH_QUEUE_PRIORITY_LOWNSQualityOfServiceUtility花费时间稍多比如下载,需要几秒或几分钟的DISPATCH_QUEUE_PRIORITY_BACKGROUNDNSQualityOfServiceBackground不可见在后台的操作可能需要好几分钟甚至几小时的

参考资料

WWDC

  • Building Responsive and Efficient Apps with GCD:https://developer.apple.com/videos/play/wwdc2015-718/

文档

  • 官方文档:https://developer.apple.com/library/prerelease/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/


文/星光社的戴铭(简书作者)
原文链接:http://www.jianshu.com/p/fbe6a654604c
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
0 0