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 方法是在主線程中調(diào)用。

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

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

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

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

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

繼續(xù)執(zhí)行任務4,異步的,所以在隊列新創(chuàng)建的線程中執(zhí)行。同時添加任務6到隊列中。
繼續(xù)執(zhí)行任務5,異步的,所以在隊列新創(chuàng)建的線程中執(zhí)行。
繼續(xù)執(zhí)行任務6,異步的,所以在隊列新創(chuàng)建的線程中執(zhí)行。

如果把 任務4 改為sync 同步的 會造成死鎖現(xiàn)象

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

其中比較重要的知識點:

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

再看一個例子,理解下為什么主線程添加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");
    });
});

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

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

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

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容