Skip to content

Commit fc86e64

Browse files
committed
调整文章中 Demo3、Demo5、Demo6展示的时机。对应的章节是【第三步:处理Dispatch
Source的暂停与恢复操作】、【`Dispatch Semaphore` 信号量】
1 parent 8d3841d commit fc86e64

2 files changed

Lines changed: 199 additions & 68 deletions

File tree

01_Parse的多线程处理思路/Parse的底层多线程处理思路.md

Lines changed: 199 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,78 @@ dispatch_suspend(queue);
196196
dispatch_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
820829
2015-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

Comments
 (0)