iOS復雜串行隊列任務分析

不說廢話,直接上代碼

- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("a", NULL);
    dispatch_async(queue, ^{
        printf("1\n");
        printf("1-%f\n", CACurrentMediaTime());
        NSLog(@"1-%@", [NSThread currentThread]);
        sleep(1);
        dispatch_async(queue, ^{
            printf("4\n");
            printf("4-%f\n", CACurrentMediaTime());
            NSLog(@"4-%@", [NSThread currentThread]);
            sleep(1);
            dispatch_async(queue, ^{
                printf("6\n");
                printf("6-%f\n", CACurrentMediaTime());
                NSLog(@"6-%@", [NSThread currentThread]);
                sleep(1);
                printf("7\n");
                printf("7-%f\n", CACurrentMediaTime());
            });
        });
    });
    dispatch_async(queue, ^{
        printf("2\n");
        printf("2-%f\n", CACurrentMediaTime());
        NSLog(@"2-%@", [NSThread currentThread]);
        sleep(1);
        dispatch_async(queue, ^{
            printf("5\n");
            printf("5-%f\n", CACurrentMediaTime());
            NSLog(@"5-%@", [NSThread currentThread]);
            sleep(1);
        });
    });
    dispatch_sync(queue, ^{
        printf("3\n");
        printf("3-%f\n", CACurrentMediaTime());
        NSLog(@"3-%@", [NSThread currentThread]);
        NSLog(@"3-mainThread-%@",[NSThread mainThread]);
        sleep(1);
    });
}

test 方法是在主線程中調用。

結論,上述代碼會依次執行 1,2,3,4,5,6,7
其中,1,2,3會卡UI,主線程卡3秒。所有任務執行總共耗時6秒。
任務3,是在主線程中執行。其余任務是在串行隊列的子線程中執行。

整個過程分析:
首先創建一個 串行隊列 隊列本身會開辟一個新線程

第一個 在主線程中 添加異步任務 添加到隊列中
第二個 在主線程中 添加異步任務 添加到隊列中
第三個 在主線程中 添加同步任務 添加到隊列中

主線程中,創建的同步任務,會導致主線程卡死,等待這第三個任務執行完畢
因為第三個任務是在線程隊列中,線程隊列中的任務按照FIFO的原則執行
第三個任務想要執行完畢,需要前兩個任務先執行完畢

串行隊列中的任務,依次執行:
先執行任務1,異步的,所以在隊列新創建的線程中執行。同時添加異步任務4到隊列中。
再執行任務2,異步的,所以在隊列新創建的線程中執行。同時添加異步任務5到隊列中。
再執行任務3,同步的,所以在 主線程 中執行。(主線程調用的同步任務)

繼續執行任務4,異步的,所以在隊列新創建的線程中執行。同時添加任務6到隊列中。
繼續執行任務5,異步的,所以在隊列新創建的線程中執行。
繼續執行任務6,異步的,所以在隊列新創建的線程中執行。

如果把 任務4 改為sync 同步的 會造成死鎖現象

因為添加任務4 的時候,是在隊列創建的那個新線程里添加的。所以隊列創建的新線程,會等待任務4完成,才可以繼續執行。但是任務4,在隊列中是第四位,他前面的任務1、2、3都還沒有執行完成。 這樣線程等待任務4完成,才能繼續執行任務1、2、3。但是任務1、2、3完不成,任務4又得不到執行。這就死鎖了。

其中比較重要的知識點:

  1. 串行隊列,只會創建一個新線程。無論異步任務還是同步任務,都會添加到這個隊列里,按照先進先出的原則依次執行。
  2. async 或者 sync,是針對調用這個任務的線程而言的。

再看一個例子,理解下為什么主線程添加dispatch_sync會死鎖

dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
    //do something
    NSLog(@"1111");
    dispatch_sync(queue, ^{
        //啥也干不了
        NSLog(@"2222");
    });
});

先對以上代碼塊做分析
首先創建一個 串行隊列,隊列本身會開辟一個新線程
任務1 sync是在主線程中調用執行,被添加到一個新的串行隊列中。
因為串行隊列中沒有別的任務,所以任務1得到執行,打印1111,同時添加任務2 到這個新的串行隊列中。
注意,此時任務1,并沒有執行完成。因為它的代碼塊還沒有全部被執行完畢,只是執行了打印1111的那部分。

同步任務2 是被包含在任務1 的代碼塊之內,因為任務1 是在主線程中調用執行的,所以任務2 也是主線程中被調用執行。任務2 被添加到新創建的串行隊列中。
此時,主線程等待任務2,執行完成。因為任務2 與任務1 是在同一個新創建的串行隊列中,隊列中的任務以FIFO的規則執行。所以任務2 想要得到執行,任務1 需要先執行完畢才行。但由于是sync,任務1 又在等待任務2 的執行完畢。所以,會發生死鎖。

理解了上述代碼,再來看下主線程中調用dispatch_sync,添加到主隊列的過程

dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"3333");
    });

dispatch_get_main_queue() 是系統提供的,與主線程綁定的一個默認串行隊列。
主線程中運行的所有任務都是被添加到這個隊列中。
當在viewDidLoad中,執行上述方法時,可以把上述代碼翻譯為以下代碼

dispatch_sync(dispatch_get_main_queue(), ^{
    //viewDidLoad
    [super viewDidLoad];
    // viewDidLoad中的其他代碼
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"3333");
    });
});

經過上面的翻譯過程,以及前一段代碼的解釋,那么接下來,事情是不是很明朗了呢。(^_^)

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

推薦閱讀更多精彩內容