GCD死锁大作战

GCD死锁大作战

什么是死锁

死锁(deadlock) 通常是当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。此时就导致了deadlock!
dead-lock.png

串行与并行

在使用GCD的时候,我们会把需要处理的任务放到Block中,然后将任务追加到相应的队列里面,这个队列,叫做Dispatch Queue。然而,存在于两种Dispatch Queue,一种是要等待上一个执行完,再执行下一个的Serial Dispatch Queue,这叫做串行队列;另一种,则是不需要上一个执行完,就能执行下一个的Concurrent Dispatch Queue,叫做并行队列。这两种,均遵循FIFO原则

dispatch_get_global_queue为全局并行队列

dispatch_get_main_queue为主线程串行队列

自己也可以创建队列:

//创建串行队列
dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL)

//创建并行队列
dispatch_queue_create("com.demo.concurrentQueue", DISPATCH_QUEUE_CONCURRENT)

同步和异步

首先同步异步是对于线程来说的,主要区别是是否等待IO操作(并不一定是)执行完成,即是否阻塞当前线程,其中的 IO 操作肯定是别的线程去执行的

并发执行的多个任务中可能存在多个线程,其中的多个线程有可能是同步执行的 也有可能是异步执行的

//同步线程
dispatch_sync(某个队列信息,^(block))

//异步线程
dispatch_async(某个队列信息,^(block))

让我们来解锁吧

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

1
2
3
4
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});

这里的死锁不是因为在主线程执行,而是在main_queue内部的两个任务:dispatch_sync和NSLog相互等待导致。NSLog(@”2”)要等dispatch_sync执行完,而在执行NSLog(@”2”)执行完以前,dispatch_sync又不会结束。

实际上,这里在主线程运行(串行线程),在主线程(当前串行队列)同步执行打印2时会产生互相等待而死锁。

1
2
3
4
5
6
NSLog(@"1");
//3会等2,因为2在全局并行队列里,不需要等待3,这样2执行完回到主队列,3就开始执行
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2");
});
NSLog(@"3");

这里不死锁,同步任务在全局并行队列中,因此2执行完就跳转到主线程执行3。

1
2
3
4
5
6
7
8
9
10
11
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(serialQueue, ^{
NSLog(@"2");
//串行队列里面同步一个串行队列就会死锁
dispatch_sync(serialQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
1
2
3
4
5
6
7
8
9
10
11
12
13
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
//回到主线程发现死循环后面就没法执行了
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
});
NSLog(@"4");
//死循环
while (1) {
//
}

以上部分欢迎讨论

参考资料

并发编程
Effective Objective-C Notes:GCD 实现同步锁
细说GCD(Grand Central Dispatch)如何用