@@ -196,6 +196,78 @@ dispatch_suspend(queue);
196196dispatch_resume (source);
197197 ```
198198
199+ 为了方便理解 `dispatch_suspend` 函数的作用,这里提供一个 Demo:Demo3, 看下运行效果:
200+
201+ 思考下NSLog的打印顺序为什么会是这样?
202+
203+
204+ 详见 Demo3(Demo_03_对DispatchQueue实现取消恢复操作_简单版):
205+
206+
207+ ```Objective-C
208+ - (void)viewDidLoad {
209+ [super viewDidLoad];
210+
211+ dispatch_queue_t queue1 = dispatch_queue_create("com.iOSChengXuYuan.queue1", 0);
212+ dispatch_queue_t queue2 = dispatch_queue_create("com.iOSChengXuYuan.queue2", 0);
213+ dispatch_group_t group = dispatch_group_create();
214+
215+ dispatch_async(queue1, ^{
216+ NSLog(@"任务 1 : queue 1...");
217+ sleep(1);
218+ NSLog(@"✅完成任务 1");
219+ });
220+
221+ dispatch_async(queue2, ^{
222+ NSLog(@"任务 1 : queue 2...");
223+ sleep(1);
224+ NSLog(@"✅完成任务 2");
225+ });
226+
227+ dispatch_group_async(group, queue1, ^{
228+ NSLog(@"🚫正在暂停 1");
229+ dispatch_suspend(queue1);
230+ });
231+ dispatch_group_async(group, queue2, ^{
232+ NSLog(@"🚫正在暂停 2");
233+ dispatch_suspend(queue2);
234+ });
235+
236+ dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
237+ NSLog(@"=======等待两个queue完成, 再往下进行...");
238+ dispatch_async(queue1, ^{
239+ NSLog(@"任务 2 : queue 1");
240+ });
241+ dispatch_async(queue2, ^{
242+ NSLog(@"任务 2 : queue 2");
243+ });
244+ NSLog(@"🔴为什么这个NSLog会在上面两个NSLog之前打印❓❓答:dispatch_suspend的作用‼️");
245+
246+ dispatch_resume(queue1);
247+ dispatch_resume(queue2);
248+ }
249+ ```
250+
251+ 打印:
252+
253+ ``` Objective-C
254+ 2015 -09 -06 02 :44 :59.614 CYLDispatchQueueSuspendTest[1610 :116662 ] 任务 1 : queue 2 ...
255+ 2015 -09 -06 02 :44 :59.613 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 1 : queue 1 ...
256+ 2015 -09 -06 02 :45 :00.614 CYLDispatchQueueSuspendTest[1610 :116665 ] ✅完成任务 1
257+ 2015 -09 -06 02 :45 :00.614 CYLDispatchQueueSuspendTest[1610 :116662 ] ✅完成任务 2
258+ 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116662 ] 🚫正在暂停 2
259+ 2015 -09 -06 02 :45 :00.615 CYLDispatchQueueSuspendTest[1610 :116665 ] 🚫正在暂停 1
260+ 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116515 ] =======等待两个queue完成, 再往下进行...
261+ 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116515 ] 🔴为什么这个NSLog 会在上面两个NSLog 之前打印❓❓答:dispatch_suspend的作用‼️
262+ 2015 -09 -06 02 :45 :00.617 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 2 : queue 1
263+ 2015 -09 -06 02 :45 :00.619 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 2 : queue 2
264+ ```
265+
266+ 思考下NSLog的打印顺序为什么会是这样?答:dispatch_suspend的作用!
267+
268+ 详见 Demo3(Demo_03_对DispatchQueue实现取消恢复操作_简单版)。
269+
270+
199271### 第四步:向` Dispatch Source ` 发送事件
200272
201273恢复源后,就可以像下面的代码片段这样,通过 ` dispatch_source_merge_data ` 向分派源发送事件:
@@ -518,6 +590,7 @@ dispatch_resume (source);
518590
519591### 让 Dispatch Source 与 Dispatch Queue 同时实现暂停和恢复
520592
593+ 本节代码详见 Demo4(Demo_04_对DispatchQueue实现取消恢复操作_综合版)
521594
522595你可能已经发现了:上面的代码是有问题的,它只是一种“假暂停”的状态。for 循环还是要执行100变,循环的次数并没有你的暂停而暂停,这在实际开发中是不允许的,因为真正的性能瓶颈永远会是在这里,这样的暂停毫无意义。那么如何让 for 循环随时可以暂停?
523596
@@ -624,7 +697,7 @@ dispatch_async(queue, ^{
624697 ```
625698
626699![ enter image description here] ( http://i61.tinypic.com/33m06er.jpg )
627-
700+ 详见 Demo4(Demo_04_对DispatchQueue实现取消恢复操作_综合版)
628701
629702
630703## Parse “离线存储对象”操作的代码摘录
@@ -714,70 +787,6 @@ dispatch_async(queue, ^{
714787
715788为了展示作用,举个反例:
716789
717- 详见 Demo3(Demo_03_对DispatchQueue实现取消恢复操作_简单版):
718-
719-
720- ``` Objective-C
721- - (void )viewDidLoad {
722- [super viewDidLoad];
723-
724- dispatch_queue_t queue1 = dispatch_queue_create("com.iOSChengXuYuan.queue1", 0);
725- dispatch_queue_t queue2 = dispatch_queue_create("com.iOSChengXuYuan.queue2", 0);
726- dispatch_group_t group = dispatch_group_create();
727-
728- dispatch_async (queue1, ^{
729- NSLog(@"任务 1 : queue 1...");
730- sleep(1);
731- NSLog(@"✅完成任务 1");
732- });
733-
734- dispatch_async(queue2, ^{
735- NSLog(@"任务 1 : queue 2...");
736- sleep(1);
737- NSLog(@"✅完成任务 2");
738- });
739-
740- dispatch_group_async(group, queue1, ^{
741- NSLog(@"🚫正在暂停 1");
742- dispatch_suspend(queue1);
743- });
744- dispatch_group_async(group, queue2, ^{
745- NSLog(@"🚫正在暂停 2");
746- dispatch_suspend(queue2);
747- });
748-
749- dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
750- NSLog(@"=======等待两个queue完成, 再往下进行...");
751- dispatch_async(queue1, ^{
752- NSLog(@"任务 2 : queue 1");
753- });
754- dispatch_async(queue2, ^{
755- NSLog(@"任务 2 : queue 2");
756- });
757- NSLog(@"🔴为什么这个NSLog会在上面两个NSLog之前打印❓❓答:dispatch_suspend的作用‼️");
758-
759- dispatch_resume(queue1);
760- dispatch_resume(queue2);
761- }
762- ```
763-
764- 打印:
765-
766- ``` Objective-C
767- 2015 -09 -06 02 :44 :59.614 CYLDispatchQueueSuspendTest[1610 :116662 ] 任务 1 : queue 2 ...
768- 2015 -09 -06 02 :44 :59.613 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 1 : queue 1 ...
769- 2015 -09 -06 02 :45 :00.614 CYLDispatchQueueSuspendTest[1610 :116665 ] ✅完成任务 1
770- 2015 -09 -06 02 :45 :00.614 CYLDispatchQueueSuspendTest[1610 :116662 ] ✅完成任务 2
771- 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116662 ] 🚫正在暂停 2
772- 2015 -09 -06 02 :45 :00.615 CYLDispatchQueueSuspendTest[1610 :116665 ] 🚫正在暂停 1
773- 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116515 ] =======等待两个queue完成, 再往下进行...
774- 2015 -09 -06 02 :45 :00.616 CYLDispatchQueueSuspendTest[1610 :116515 ] 🔴为什么这个NSLog 会在上面两个NSLog 之前打印❓❓答:dispatch_suspend的作用‼️
775- 2015 -09 -06 02 :45 :00.617 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 2 : queue 1
776- 2015 -09 -06 02 :45 :00.619 CYLDispatchQueueSuspendTest[1610 :116665 ] 任务 2 : queue 2
777- ```
778-
779-
780-
781790 ``` Objective-C
782791 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
783792 NSMutableArray *array = [[NSMutableArray alloc ] init ];
@@ -820,11 +829,10 @@ CYLDispatchSemaphoreTest(10384,0x112d43000) malloc: *** error for object 0x7f898
8208292015 -09 -07 00 :42 :52.734 CYLDispatchSemaphoreTest[10438 :780505 ] 99949
821830 ```
822831
823-
832+ 这种资源抢夺的情况,一般的做法是使用串行队列,或者像下面一样的同步队列,得以解决:
824833
825834 ``` Objective-C
826835 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
827- dispatch_semaphore_t semaphore = dispatch_semaphore_create(1 ) ;
828836 NSMutableArray *array = [[NSMutableArray alloc ] init ];
829837 for (int i = 0 ; i< 100000 ; ++i) {
830838 dispatch_sync (queue, ^{
@@ -834,7 +842,129 @@ CYLDispatchSemaphoreTest(10384,0x112d43000) malloc: *** error for object 0x7f898
834842 }
835843 NSLog(@"%@", @([array count]));
836844 ```
837- 详见 Demo3(Demo_03_对DispatchQueue实现取消恢复操作_简单版):
845+
846+ 下面展示下展示使用 `dispatch_semaphore_t` 的解决方案:
847+
848+
849+
850+ `dispatch_semaphore_t` 的作用之一就是解决这种资源抢夺的情况,下面展示下使用 `dispatch_semaphore_t` 实现一个资源锁:
851+
852+ 以下源码详见 Demo6(Demo_06_展示dispatch_semaphore_t基本用法)
853+
854+
855+ ```Objective-C
856+ - (void)viewDidLoad {
857+ [super viewDidLoad];
858+ //因为用到了dispatch_barrier_async,该函数只能搭配自定义并行队列dispatch_queue_t使用。所以不能使用:dispatch_get_global_queue
859+ dispatch_queue_t queue = dispatch_queue_create("com.ioschengxuyuan.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
860+ /*
861+ *
862+ *生成Dispatch Semaphore
863+ Dispatch Semaphore 的计数初始值设定为“1”
864+ (该初始值的1与下文中两个函数dispatch_semaphore_wait与dispatch_semaphore_signal进行的减1、加1里的1没有必然联系。
865+
866+ 就算初始值是100,两个函数dispatch_semaphore_wait与dispatch_semaphore_signal还是会减“1”、加“1”)。
867+ 保证可访问 NSMutableArray 类对象的线程
868+ 同时只能有1个
869+ *
870+ */
871+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(1) ;
872+ NSMutableArray *array = [[NSMutableArray alloc] init];
873+ for(int i = 0; i< 100000; ++i) {
874+ dispatch_async(queue, ^{
875+ /*
876+ *
877+ *等待Dispatch Semaphore
878+ *一直等待,直到Dispatch Semaphore的计数值达到大于等于1
879+ */
880+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) ;
881+ /*
882+ *由于Dispatch Semaphore的计数值达到大于等于1
883+ *所以将Dispatch Semaphore的计数值减去1
884+ *dispatch_semaphore_wait 函数执行返回。
885+ *即执行到此时的
886+ *Dispatch Semaphore 的计数值恒为0
887+ *
888+ *由于可访问NSMutaleArray类对象的线程
889+ *只有一个
890+ *因此可安全地进行更新
891+ *
892+ */
893+ NSLog(@"🔴%@",[NSThread currentThread]);
894+ [array addObject:[NSNumber numberWithInt:i]];
895+ /*
896+ *
897+ *排他控制处理结束,
898+ *所以通过dispatch_semaphore_signal函数
899+ *将Dispatch Semaphore的计数值加1
900+ *如果有通过dispatch_semaphore_wait函数
901+ *等待Dispatch Semaphore的计数值增加的线程,
902+ ★就由最先等待的线程执行。
903+ */
904+ dispatch_semaphore_signal(semaphore);
905+ });
906+ }
907+ /*
908+ *
909+ 等为数组遍历添加元素后,检查下数组的成员个数是否正确
910+ *
911+ */
912+ dispatch_barrier_async(queue, ^{
913+ NSLog(@"🔴类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, @([array count]));
914+ });
915+ }
916+ ```
917+
918+
919+
920+
921+ 详见 Demo5(Demo_05_展示dispatch_semaphore_t基本用法)
922+
923+ ``` Objective-C
924+ - (void )viewDidLoad {
925+ [super viewDidLoad];
926+
927+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
928+ // dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
929+
930+ dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);//等待一秒
931+ //dispatch_time_t time = DISPATCH_TIME_FOREVER;//永久等待
932+ NSLog(@"begin ==> 车库开始营业了!");
933+ /*
934+ *
935+ 如果 semphore 的值等于0,就阻塞1秒钟,才会往下照常进行;
936+ 如果大于等于1则往下进行并将 semphore 进行减1处理。
937+ *
938+ * /
939+ long result = dispatch_semaphore_wait(semaphore, time);
940+ if (result == 0) {
941+ /*
942+ *
943+ * 由子Dispatch Semaphore的计数值达到大于等于1
944+ * 或者在待机中的指定时间内
945+ * Dispatch Semaphore的计数值达到大于等于1
946+ 所以Dispatch Semaphore的计数值减去1
947+ 可执行需要进行排他控制的处理.
948+ 可以理解为:没有阻塞的线程了。
949+ 就好比:车库有一个或一个以上的车位,只来了一辆车,所以“无需等待”
950+ *
951+ * /
952+ NSLog(@"result = 0 ==> 有车位,无需等待!==> 在这里可安全地执行【需要排他控制的处理(比如只允许一条线程为mutableArray进行addObj操作)】");
953+ dispatch_semaphore_signal(semaphore);//使用signal以确保编译器release掉dispatch_semaphore_t时的值与初始值一致, 否则会EXC_BAD_INSTRUCTION ,见http://is.gd/EaJgk5
954+ } else {
955+ /*
956+ *
957+ * 由于Dispatch Semaphore的计数值为0
958+ .因此在达到指定时间为止待机
959+ 这个else里发生的事情,就好比:车库没车位,来了一辆车,等待了半个小时后,做出的一些事情。
960+ 比如:忍受不了,走了。。
961+ *
962+ * /
963+ NSLog(@"result != 0 ==> timeout,deadline,忍受不了,走了。。");
964+
965+ }
966+ }
967+ ```
838968
839969
840970### 在项目中的应用:强制把异步任务转换为同步任务来方便进行单元测试
@@ -899,6 +1029,7 @@ CYLDispatchSemaphoreTest(10384,0x112d43000) malloc: *** error for object 0x7f898
8991029耐心的极限时间 | 超时时间 | `dispatch_semaphore_wait` |
9001030逛街结束走了,离开车位 | signal+1 | `dispatch_semaphore_signal` |
9011031
1032+
9021033### 使用`Dispatch Semaphore`控制并发线程数量
9031034
9041035正如文章开头所说:从 iOS7 升到 iOS8 后,GCD 出现了一个重大的变化:在 iOS7 时,使用 GCD 的并行队列, `dispatch_async` 最大开启的线程一直能控制在6、7条,线程数都是个位数,然而 iOS8后,最大线程数一度可以达到40条、50条。然而在文档上并没有对这一做法的目的进行介绍。
0 commit comments