GCD 死锁

死锁可能导致iOS界面卡死。

主队列上的死锁

viewDidAppear方法中写,是为了不让界面卡死。

1
2
3
4
5
6
7
8
9
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"Task 1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Task 2");
});
NSLog(@"Task 3");
}

控制台输出结果

1
Task 1

分析

Task 2追加到主队列中,同步调用,会等待,所以任务Task 3在等待Task 2执行完毕,而Task 2也在等待排在它之前的Task 3执行完毕,相互等待。造成死锁。

自定义串行队列

第一种情况

调用2次同步函数。

1
2
3
4
5
6
7
8
9
dispatch_queue_t queue = dispatch_queue_create("com.lucaslz.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"Task 1");
dispatch_sync(queue, ^{
NSLog(@"Task 2");
});
NSLog(@"Task 3");
});

控制台输出结果

1
Task 1

分析

首先是Task 1同步任务Task 3一起当做一个任务加入串行队列中。同步调用,表示会等待。当执行了Task1,又把Task 2加入串行队列中,这时候又是同步调用,又会等待前一个任务执行完毕。所以外层的任务等待Task 2,相互等待,造成死锁。

第二种情况

先异步调用,在同步调用。

1
2
3
4
5
6
7
dispatch_async(queue, ^{
NSLog(@"Task 1");
dispatch_sync(queue, ^{
NSLog(@"Task 2");
});
NSLog(@"Task 3");
});

控制台输出结果

1
Task 1

分析

因为串行队列是有顺序执行的。
Task 1同步任务Task 3一起当做一个任务加入串行队列。当执行了Task 1后,又把Task 2加入到串行队列中,又需要等待队列中的前一个任务(即Task 1同步任务Task 3)执行。而前一个任务又需要等待Task 2,相互等待,造成死锁。

结论

只有在串行队列(主队列也是串行队列)中才有可能发生死锁,并且还是嵌套调用。实际上一般都直接使用异步调用,所以基本上不会遇见发生死锁。

坚持原创技术分享,您的支持将鼓励我继续创作!