先看代碼
- (void)test9 {
dispatch_queue_t myQueue = dispatch_queue_create("come.my.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_sync(myQueue, ^{
NSLog(@"+++++++++ 1");
dispatch_sync(myQueue, ^{
NSLog(@"+++++++++ 2");
});
});
}
控制臺只會打印
2018-01-03 16:35:01.315736+0800 GCD練習[3327:197445] +++++++++ 1
因為產生了死鎖
猜測下,myQueue
的里面有一把鎖,這把鎖確保當隊列是serial dispatch queue(串行)時,只有一個block執行,當執行到block 1時(按照先后順序命名為 block 1, block 2),開始加鎖,只有當block 1執行完,才會釋放。但是同時block 1又同步派發了一個任務block 2,啥意思,同步派發就意味著block 1會被阻塞,直到block 2被執行完成;
問題來了,block 2要執行的前提就是myQueue
的這把鎖被block 1釋放,但block 1釋放這把鎖的前提又是block 1執行完成,OK 完美的死循環。
文字看著有點頭痛。先上一段偽代碼吧
- (void)test10 {
dispatch_queue_t queue = dispatch_queue_create("haha", DISPATCH_QUEUE_SERIAL);
//同步
dispatch_sync(queue, block)
{
if (queue == 串行隊列) {
lock(queue.lock);//每個串行queue都有自己唯一的鎖
}
//調用block
block();
//解鎖
if (queue == 串行隊列) {
unlock(queue.lock);//每個串行queue都有自己唯一的鎖
}
}
//異步
dispatch_async(queue, block){
if (queue != mainQueue) {
//創建新的線程,執行任務
} else {
//那就不創建新線程,直接執行下面的代碼了
}
//只有串行隊列才會上鎖
if (queue == 串行隊列) {
lock(queue.lock);//每個串行queue都有自己唯一的鎖
}
if (queue == 并行隊列) {
//是并行隊列。就創建新線程
Thread myThread = createNewThread();
//在新的線程李執行任務 block();
myThread(
//調用block
block();
) else {//不是并行隊列
//調用block
block();
}
}
//解鎖
if (queue == 串行隊列) {
unlock(queue.lock);
}
}
}
現在就用偽代碼來解釋下開頭的例子
- (void)test11 {
dispatch_queue_t myQueue = dispatch_queue_create("come.my.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_sync(myQueue, ^{//設置為block 1
if (myQueue == 串行隊列) {
//上鎖
lock(myQueue.lock);
}
NSLog(@"+++++++++ 1");
dispatch_sync(myQueue, ^{//設置為block 2
if (myQueue == 串行隊列) {
lock(myQueue.lock);//block 2不能上鎖,因為block 1已經lockle ,而且block 1還沒有unlock
//所以造成死鎖了,block 2會等待block 1 unlock,但是block 1的unlock 必須等待block 2執行完才會調用,所以 block 2 一直在等待 block 1,block 1又在等block 2執行完 。。。
//一個完美的環
// 這里有個重點:queue.lock每個queue有自己唯一的lock
//所以只有同一個queue的任務(block)才會造成死鎖,因為lock()的參數,如果是不同的鎖,肯定是可以上鎖成功的,
}
NSLog(@"+++++++++ 2");
if (myQueue == 串行隊列) {
unlock(myQueue.lock);
}
});
});
}
那要怎么執行第二步呢
目前我想到了有兩種
- 將隊列由
DISPATCH_QUEUE_SERIAL
修改為DISPATCH_QUEUE_CONCURRENT
- 嵌套使用串行隊列(同一個),會產生死鎖,所以將第二個隊列替換為
dispatch_get_main_queue
那什么情況會發生死鎖呢,異步執行block可定不會產生死鎖,
修改下代碼:
dispatch_async(dispatch_get_global_queue(0,0), ^(void){
NSLog(@"我進來了");
});
總結一點,異步不會產生死鎖。
產生死鎖的代碼
dispatch_sync(dispatch_get_main_queue(), ^(void){
NSLog(@"這里死鎖了");
});
產生死鎖的原因,最重要的一點就是在主線程里執行dispatch_sync
方法,這是個同步方法,block執行完是不會返回。如果是異步的,那就是立即返回,就不會阻塞主線程了。
所以,在GCD中需要關心的是同步還是異步執行,以及把block加到哪個隊列中(串行還是并發)
之前有講到,在同步執行時,向并發隊列添加任務,是不會產生死鎖的。
導致死鎖的原因是在串行隊列中添加block,而block一直在等前面的任務處理完才會執行,導致死鎖。
產生死鎖的原因猜測是:
在某一個串行隊列中,同步的向這個隊列中添加block
比如同步的向一個串行隊列添加任務,并不一定會死鎖,比如
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
dispatch_sync(serialQueue, ^{
NSLog(@"開啟了一個異步任務task,當前線程:%@", [NSThread currentThread]);
});
分析一下代碼,dispatch_sync不是將主線程給阻塞了嗎,那為什么serialQueue中的任務還能在主線程里執行
猜想哈 在主線程里有兩個隊列,一個是main thread queue ,另一個是serialQueue,現在dispatch_sync只是把main queue給阻塞了,但serialQueue沒有被阻塞,所以serialQueue中的任務任然可以在主線程執行