GCD死鎖問題的解決

串行與並行

**串行和並行都是相對於隊列而言的 **

-隊列(負責調度任務)
-串行隊列:一個接一個的調度任務
-併發隊列:可以同時調度多個任務

在使用GCD的時候,我們會把需要處理的任務放到Block中,然後將任務追加到相應的隊列裏面,這個隊列,叫做Dispatch Queue
隊列一般存在於兩種Dispatch Queue
一種是要等待上一個執行完,再執行下一個的Serial Dispatch Queue,這叫做串行隊列
另一種,則是不需要上一個執行完,就能執行下一個的Concurrent Dispatch Queue,叫做並行隊列
這兩種,均遵循FIFO原則,也就是先進先出原則。

那麼,並行隊列又是怎麼在執行呢?
並行隊列雖然可以同時多個任務的處理,但是並行隊列的處理量,還是要根據當前系統狀態來。如果當前系統狀態最多處理2個任務,那麼1、2會排在前面,3什麼時候操作,就看1或者2誰先完成,然後3接在後面。

 同步與異步

串行與並行針對的是隊列,而同步與異步,針對的則是線程。
最大的區別在於,同步線程要阻塞當前線程,必須要等待同步線程中的任務執行完,返回以後,才能繼續執行下一任務;而異步線程則是不用等待。

GCD API

系統標準提供的兩個隊列

// 全局隊列,一個特殊的並行隊列
dispatch_get_global_queue
// 主隊列,在主線程中運行,因爲主線程只有一個,所以這是一個特殊的串行隊列
dispatch_get_main_queue


自己生成隊列

// 從DISPATCH_QUEUE_SERIAL看出,這是串行隊列
dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL)
// 同理,這是一個並行隊列
dispatch_queue_create("com.demo.concurrentQueue", DISPATCH_QUEUE_CONCURRENT)


同步與異步線程的創建:

dispatch_sync(..., ^(block)) // 同步線程
dispatch_async(..., ^(block)) // 異步線程

一、當同步遇到了串行

NSLog(@"1"); // 任務1
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"2"); // 任務2
});
NSLog(@"3"); // 任務3

輸出結果:1

分析:

首先執行任務1,這是肯定沒問題的,只是接下來,程序遇到了同步線程,那麼它會進入等待,等待任務2執行完,然後執行任務3。但這是主隊列,是一個特殊的串行隊列,有任務來,當然會將任務加到隊尾,然後遵循FIFO原則執行任務。那麼,現在任務2就會被加到最後,任務3排在了任務2前面,於是就造成了相互等待的結果。

二、當同步遇到了並行

NSLog(@"1"); // 任務1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"2"); // 任務2
});
NSLog(@"3"); // 任務3

輸出結果:1,2,3

分析:

首先執行任務1,接下來會遇到一個同步線程,程序會進入等待。等待任務2執行完成以後,才能繼續執行任務3。從dispatch_get_global_queue可以看出,任務2被加入到了全局的並行隊列中,當並行隊列執行完任務2以後,返回到主隊列,繼續執行任務3。

三、同步異步同時存在

dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任務1
dispatch_async(queue, ^{
    NSLog(@"2"); // 任務2
    dispatch_sync(queue, ^{  
        NSLog(@"3"); // 任務3
    });
    NSLog(@"4"); // 任務4
});
NSLog(@"5"); // 任務5

輸出結果:1,5,2         3和4沒有輸出

分析:

這個案例沒有使用系統提供的串行或並行隊列,而是自己通過dispatch_queue_create函數創建了一個DISPATCH_QUEUE_SERIAL的串行隊列。
1.執行任務1;
2.遇到異步線程,將【任務2、同步線程、任務4】加入串行隊列中。因爲是異步線程,所以在主線程中的任務5不必等待異步線程中的所有任務完成;
3.因爲任務5不必等待;
4.任務2執行完以後,遇到同步線程,這時,將任務3加入串行隊列;
5.又因爲任務4比任務3早加入串行隊列,所以,任務3要等待任務4完成以後,才能執行。但是任務3所在的同步線程會阻塞,所以任務4必須等任務3執行完以後再執行。這就又陷入了無限的等待中,造成死鎖。

四、異步執行,同步回主線程

NSLog(@"1"); // 任務1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"2"); // 任務2
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"3"); // 任務3
    });
    NSLog(@"4"); // 任務4
});
NSLog(@"5"); // 任務5

輸出結果:1,5,2,3,4

分析:

首先,將【任務1、異步線程、任務5】加入Main Queue中,異步線程中的任務是:【任務2、同步線程、任務4】。

所以,先執行任務1,然後將異步線程中的任務加入到Global Queue中,因爲異步線程,所以任務5不用等待,結果就是2和5的輸出順序不一定。

然後再看異步線程中的任務執行順序。任務2執行完以後,遇到同步線程。將同步線程中的任務又回調加入到Main Queue中,這時加入的任務3在任務5的後面。

當任務3執行完以後,沒有了阻塞,程序繼續執行任務4。

從以上的分析來看,得到的幾個結果:1最先執行;2和5順序不一定;4一定在3後面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章