@@ -16,7 +16,7 @@ Cocoa 中封装了 NSThread, NSOperation, GCD 三种多线程编程方式,他
1616
1717* NSOperation
1818
19- NSOperation 是一个抽象类,它封装了线程的细节实现,不需要自己管理线程的生命周期和线程的同步和互斥等。只是需要关注自己的业务逻辑处理,需要和 NSOperationQueue 一起使用。使用 NSOperation 时,你可以很方便的设置线程之间的依赖关系。这在略微复杂的业务需求中尤为重要。(在此后的GCD烧脑体操中,你将会深刻体会)
19+ NSOperation 是一个抽象类,它封装了线程的细节实现,不需要自己管理线程的生命周期和线程的同步和互斥等。只是需要关注自己的业务逻辑处理,需要和 NSOperationQueue 一起使用。使用 NSOperation 时,你可以很方便的设置线程之间的依赖关系。这在略微复杂的业务需求中尤为重要。
2020
2121* GCD
2222
@@ -172,15 +172,13 @@ printf("两个 block 都已经执行完毕\n");
172172* 避免在任务中使用锁,如果使用锁的话可能会阻碍 queue 中其他 task 的运行
173173* 不建议获取 dispatch_queue 底层所使用的 thread 的有关信息,也不建议在 queue 中再使用 pthread 系函数
174174
175- #### GCD烧脑体操
175+ #### GCD 案例分析
176176
177- 说了 GCD 这么多,不如来几道烧脑体操吧,也借此机会体会一下 GCD。
178-
179- ##### 烧脑体操第一节
177+ ##### 案例一
180178
181179这是一个广为流传的例子,代码如下:
182180
183- ```
181+ ```objectivec
184182NSLog(@"1"); // 任务1
185183dispatch_sync(dispatch_get_main_queue(), ^{
186184 NSLog(@"2"); // 任务2
@@ -208,19 +206,19 @@ NSLog(@"3"); // 任务3
208206
209207主线程启动以后的加入顺序是:任务1,同步线程,任务三。执行完任务1,就会启动同步线程,然后将任务2加入队列。所以,任务3在任务2的前面。如图中所示的那样,这种情况下 任务2 与 任务 3都在等待彼此完成之后才能执行,这就造成了死锁。
210208
211- ##### 烧脑体操第二节
209+ ##### 案例二
212210
213- 这个例子由此前的第一节演化而来 ,代码如下
211+ 这个例子由此前的案例一演化而来 ,代码如下:
214212
215- ```
213+ ``` objectivec
216214NSLog (@" 1" ); // 任务1
217215dispatch_sync (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
218216 NSLog(@"2"); // 任务2
219217});
220218NSLog(@"3"); // 任务3
221219```
222220
223- 正如你所期待的那样, 这并不会造成死锁,控制台输出如下:
221+ 这并不会造成死锁,控制台输出如下:
224222
225223```
2262241
@@ -234,13 +232,13 @@ NSLog(@"3"); // 任务3
234232
235233分析与过程描述:
236234
237- 首先执行任务1,接下来会遇到一个同步线程,程序会进入等待。等待任务2执行完成以后,才能继续执行任务3。从dispatch_get_global_queue可以看出 ,任务2被加入到了全局的并行队列中,当并行队列执行完任务2以后,返回到主队列,继续执行任务3。
235+ 首先执行任务1,接下来会遇到一个同步线程,程序会进入等待。等待任务2执行完成以后,才能继续执行任务3。从 dispatch_get_global_queue 可以看出 ,任务2被加入到了全局的并行队列中,当并行队列执行完任务2以后,返回到主队列,继续执行任务3。
238236
239- ##### 烧脑体操第三节
237+ ##### 案例三
240238
241239这个例子会比此前的两节复杂一些,代码如下:
242240
243- ```
241+ ```objectivec
244242dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
245243NSLog(@"1"); // 任务1
246244dispatch_async(queue, ^{
@@ -262,7 +260,6 @@ NSLog(@"5"); // 任务5
262260// 5和2的顺序不一定
263261```
264262
265-
266263分析:这里没有使用系统提供的串行或并行队列,而是自己通过dispatch_queue_create函数创建了一个` DISPATCH_QUEUE_SERIAL ` 的串行队列。
267264
268265如图所示:
@@ -277,13 +274,11 @@ NSLog(@"5"); // 任务5
2772744 . 任务2执行完以后,遇到同步线程,这时,将任务3加入异步的串行队列
2782755 . 又因为任务4比任务3早加入串行队列,所以,任务3要等待任务4完成以后,才能执行。但是任务3所在的同步线程会阻塞,所以任务4必须等任务3执行完以后再执行。这就又陷入了无限的等待中,造成死锁。
279276
280- 怎么样,是不是有点蒙逼,那快让我们进入第四小节吧!
281-
282- ##### 烧脑体操第四节
277+ ##### 案例四
283278
284279代码如下:
285280
286- ```
281+ ``` objectivec
287282NSLog (@" 1" ); // 任务1
288283dispatch_async (dispatch_get_global_queue(0, 0), ^{
289284 NSLog(@"2"); // 任务2
@@ -322,11 +317,11 @@ NSLog(@"5"); // 任务5
322317
323318从以上的分析来看,得到的几个结果:1最先执行;2和5顺序不一定;4一定在3后面。
324319
325- ##### 烧脑体操第五节
320+ ##### 案例五
326321
327- 什么?之前的你懂看懂了?那第五节估计也难不倒你咯, 代码如下:
322+ 代码如下:
328323
329- ```
324+ ```objectivec
330325dispatch_async(dispatch_get_global_queue(0, 0), ^{
331326 NSLog(@"1"); // 任务1
332327 dispatch_sync(dispatch_get_main_queue(), ^{
@@ -364,9 +359,9 @@ NSLog(@"5"); // 任务5
364359
365360最终,只能得到1和4顺序不定的结果。
366361
367- ##### 烧脑体操总结
362+ ##### 案例总结
368363
369- 相信对于绝大多数人来说,在烧脑体操第三节开始 ,是否死锁以及整个的执行流程就变得不是那么显而易见了,没错这五节烧脑体操就意在展示 GCD 的问题:如果想要设置线程间的依赖关系,那就需要嵌套,如果嵌套就会使得非常恶心的事情发生 。这应该是 GCD 的一个非常明显的缺陷之一了。
364+ 相信对于绝大多数人来说,在案例三开始 ,是否死锁以及整个的执行流程就变得不是那么显而易见了,这五个案例就意在展示 GCD 的问题:如果想要设置线程间的依赖关系,那就需要嵌套,如果嵌套就会导致一些复杂的事情发生 。这应该是 GCD 的一个非常明显的缺陷之一了。
370365
371366当然,NSOperation 为了我们提供了很方便设置依赖关系的解决方案。
372367
@@ -566,4 +561,5 @@ typedef enum : NSInteger {
566561 * http://www.humancode.us/2014/08/14/target-queues.html
567562 * http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/
568563 * http://www.jianshu.com/p/0b0d9b1f1f19
569- * http://www.cnblogs.com/tangbinblog/p/4133481.html
564+ * http://www.cnblogs.com/tangbinblog/p/4133481.html
565+ * http://www.saitjr.com/ios/ios-gcd-deadlock.html
0 commit comments