> 什么是GCD?以下摘抄自苹果的官方说明。 Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统版中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率。 也就是说 ,GCD能够用我们难以置信的非常简洁的记述方法,实现了极为复杂繁琐的多线程编程,可以说这是一项划时代的技术。 也许你经常看到的CGD用法是这样的: ``` dispatch_async(dispatch_get_global_queue(0, 0), ^{ /** * 在这里进行一些复杂的计算 */ dispatch_async(dispatch_get_main_queue(), ^{ /** * 在这里更新你的UI视图 */ }); }); ``` 我们在一个子线程内进行一些复杂的计算,然后在主线程内显示计算结果,这个可能是一个最常见的GCD用法,然而事实上GCD还有更多的内容。 多线程编程实际上是一种易发生各种问题的编程技术。比如多个线程更新相同的资源会导致数据的不一致(数据竞争)、停止等待事件的线程会导致多个线程相互持续等待(死锁)、使用太多线程会消耗大量内存等。 当然,要回避这些问题有许多方法,但程序都偏于复杂。 尽管极易发生各种问题,也应该使用多线程编程。这是为什么呢?因为使用多线程编程可以保证应用程序的的响应性能。 应用程序在启动的时候,最先启动的执行的线程、即“主线程”来描绘用户主界面、处理触摸屏的事件等。如果在该线程中进行长时间的处理,如AR用画像的识别或数据库访问,就会妨碍主线程的执行(阻塞)。在OS X和iOS的应用程序中,会妨碍主线程中被成为RunLoop的主循环的执行,从而导致不能更新用户界面、应用程序的画面长时间停滞等问题。 使用多线程,在执行长时间的处理时仍可保证用户界面的响应性能。 使用GCD可以大大简化使用多线程编程的难度。 ------------ # 1、 dispatch_queue_create ``` dispatch_async(queue, ^{ }); ``` 我们所使用的GCD大概是长个样子的,我们可以看到第一个参数是一个是我们使用的线程,所以我们想要使用GCD的话,第一步就是声明一个线程。在这里声明线程包括两种: - Serial Dispatch Queue - Concurrent Dispatch Queue 第一种线程是等待执行中的Serial Dispatch Queue 第二种则相反,不等待执行中的Concurrent Dispatch Queue。 我们可以用 dispatch_queue_create函数来创建这个线程。 比如像这样: ``` dispatch_queue_t queue = dispatch_queue_create("com.xlx.test", DISPATCH_QUEUE_CONCURRENT); //Concurrent Dispatch Queue dispatch_queue_t queue = dispatch_queue_create("com.xlx.test", DISPATCH_QUEUE_SERIAL); //Serial Dispatch Queue ``` 在这个函数中有两个参数,第一个参数是这个线程的名字,会在调使用用到,第二个参数则是表明这个线程是何种线程。第二个参数可以指定为NULL,当你指定为NULL时生成的线程为DISPATCH_QUEUE_CONCURRENT。 我们分别用着两种线程来执行同样的代码,来看看结果有什么区别: ``` dispatch_async(queue, ^{ NSLog(@"queue1"); }); dispatch_async(queue, ^{ NSLog(@"queue2"); }); dispatch_async(queue, ^{ NSLog(@"queue3"); }); dispatch_async(queue, ^{ NSLog(@"queue4"); }); ``` 代码非常简单,用同一个queue执行多个任务,任务只是输出一个字符创。 当我们使用DISPATCH_QUEUE_CONCURRENT时输出结果为: ``` 2016-09-12 23:42:06.122 GCDTest[6077:696342] queue1 2016-09-12 23:42:06.122 GCDTest[6077:696361] queue4 2016-09-12 23:42:06.122 GCDTest[6077:696347] queue3 2016-09-12 23:42:06.122 GCDTest[6077:696333] queue2 当我们使用DISPATCH_QUEUE_SERIAL线程时,输出结果是: 2016-09-12 23:43:53.899 GCDTest[6118:698263] queue1 2016-09-12 23:43:53.900 GCDTest[6118:698263] queue2 2016-09-12 23:43:53.900 GCDTest[6118:698263] queue3 2016-09-12 23:43:53.900 GCDTest[6118:698263] queue4 ``` 我们可以看到当我们使用的线程为DISPATCH_QUEUE_SERIAL时,任务的执行顺序是依次执行的,然而当我们使用DISPATCH_QUEUE_CONCURRENT线程时四个任务并不是按添加顺序执行的,同时需要注意的是:这个执行顺序在每次执行的时候都会不一样,我们无法控制这个执行顺序。 一旦生成Serial Dispatch Queue并追加处理,系统对于一个Serial Dispatch Queue就只生成并使用一个线程。如果生成2000个Serial Dispatch Queue,那么就生成2000个线程。 像之前举例的多线程编程问题一样,如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。 只是为了避免多线程问题之一——多个线程更新相同数据导致数据竞争时使用Serial Dispatch Queue。这样就可以避免多线程的数据竞争。 ----------- # 2、Main Dispatch Queue/Global Dispatch Queue 实际上不用特意生成Dispatch Queue系统也会给我们提供几个。那就是Main Dispatch Queue和Global Dispatch Queue。 Main Dispatch Queue就是我们的主线程,我们所有的UI更新都用通过主线程来进行,否则界面响应会发生延迟。 另一个Global Dispatch Queue是所有应用程序都能够使用的Concurrent Dispatch Queue。没有必要通过dispatch_queue_create函数逐个生成Concurrent Dispatch Queue。只要获取Dispatch Queue即可。 像在本文第一段代码中,我们是这样使用Global Dispatch Queue: ``` dispatch_async(dispatch_get_global_queue(0, 0), ^{ }; ``` dispatch_get_global_queue这个函数这个函数中包涵两个参数,第一个是表明线程的优先度,Global Dispatch Queue包涵四个执行优先度,他们分别是: ``` DISPATCH_QUEUE_PRIORITY_HIGH //高优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT //默认优先级 DISPATCH_QUEUE_PRIORITY_LOW //低优先级 DISPATCH_QUEUE_PRIORITY_BACKGROUND //后台优先级 ``` -------- # 3、Dispatch Group 在追加到Dispatch Queue中的多个处理全部结束后想要执行结束处理,这种结果情况会经常出现。只使用一个Serial dispatch queue时,只要将想要执行的处理全部追加到该Serial dispatch queue中并在最后追加结束处理,即可实现。但是在使用Concurrent dispatch queue时或同时处理多个dispatch queue时,源代码就会变得颇为复杂。 在这种情况下使用Dispatch Group。我们来看下面的代码: ``` dispatch_queue_t queue = dispatch_queue_create("com.xlx.test", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"blok1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok2"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok3"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok4"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"done"); }); ``` 以上这段代码的输出结果为: ``` 2016-09-13 01:06:47.445 GCDTest[6319:727493] blok3 2016-09-13 01:06:47.445 GCDTest[6319:727498] blok4 2016-09-13 01:06:47.445 GCDTest[6319:727471] blok1 2016-09-13 01:06:47.445 GCDTest[6319:727463] blok2 2016-09-13 01:06:47.446 GCDTest[6319:727463] done ``` 我们需要声明一个dispatch_group_t然后将所有的多线程任务都追加到这个group中,在这个的group的所有任务结束之后,将会执行dispatch_group_notify,所以在输出结果中“done”永远是在最后面. 与dispatch_group_notify函数能够起到类似作用的还有dispatch_group_wait,如果我们想要在所有任务结束后执行处理,使用dispatch_group_wait函数的话我们可以这么做: ``` dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"blok1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok2"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok3"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blok4"); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"done"); ``` dispatch_group_wait的第二个参数为等待时间,在上面的代码中,我们将这个参数设置为DISPATCH_TIME_FOREVER,那么dispatch_group_wait函数将卡顿当前线程直到多线程任务全部处理完毕,所以在上面的代码中,“done”永远在最后输出。 dispatch_group_wait的第二个参数当然也可以指定为你希望等待的时间,需要注意的是这里的单位是纳秒,所以比如你希望等待一秒,那么代码应该是这样的: ``` dispatch_time_t time = 1*NSEC_PER_SEC; long result = dispatch_group_wait(group, time); if (result == 0) { /** * 多线程任务已处理完毕 * */ }else{ /** * 多线程任务还在处理当中 * */ } ``` 我们需要指定一个dispatch_time_t类型参数,dispatch_group_wait函数将返回long类型的参数,如果result为0则表示在指定时间内完成了多线程任务,反之则反。 ----------- # 4、dispatch_set_target_queue dispatch_queue_create函数生成的线程都是默认优先级,当然你可以修改这个优先度。 dispatch_queue_t myqueue = dispatch_queue_create("com.xlx.test", NULL); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_set_target_queue(myqueue, queue); -------- # 5、 dispatch_barrier_sync ``` dispatch_queue_t queue = dispatch_queue_create("com.xlx.test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"Blo1"); }); dispatch_async(queue, ^{ NSLog(@"Blo2"); }); dispatch_async(queue, ^{ NSLog(@"Blo3"); }); dispatch_barrier_sync(queue, ^{ NSLog(@"dispatch_barrier_sync"); }); dispatch_async(queue, ^{ NSLog(@"Blo4"); }); dispatch_async(queue, ^{ NSLog(@"Blo5"); }); dispatch_async(queue, ^{ NSLog(@"Blo6"); }); ``` ### 欢迎加入iOS交流群537774852