GCD死鎖之探究

先看代碼

- (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);
            }
        });
    });
}

那要怎么執行第二步呢

目前我想到了有兩種

  1. 將隊列由DISPATCH_QUEUE_SERIAL修改為DISPATCH_QUEUE_CONCURRENT
  2. 嵌套使用串行隊列(同一個),會產生死鎖,所以將第二個隊列替換為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中的任務任然可以在主線程執行

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。