GCD編程中任務的執行順序

本篇文章是對上篇文章開發多線程的一個補充。重點在于探究GCD編程中,任務的執行順序,掌握好任務的執行順序,我們才能很好的去使用GCD。串行隊列、并行隊列、同步、異步是gcd編程中四個非常重要的概念,它們的組合會產生多種不同的執行順序。下面我們來舉例探究一下。

1.串行隊列 異步任務

-(void)method_one{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_SERIAL);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@",[NSThread currentThread]);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);

}

執行結果如下:

串行隊列異步任務

分析:在方法method_one中我們首先創建了一個串行的隊列,然后使用for循環往串行隊列中追加了10個異步任務。先在主線程中執行打印start—,然后將異步任務添加到串行隊列中(只是添加,并沒有立即執行),之后在主線程打印end—,最后才會從串行隊列中依次取出一個任務,并在子線程中執行,由于串行隊列執行任務是有序的, 因此異步任務打印是有序的。所以, 主線程會在執行完dispatch_async方法后,立即返回執行主線程后續相關操作,主線程任務執行完畢后,才會在子線程中依次執行異步任務。由于串行隊列中的任務是依次取出來執行的,即前一個任務在子線程執行完畢后,才能取出后一個任務來執行,并且只創建了一個子線程。

1.2 創建多個串行隊列,將異步任務添加進去
我們將上面的代碼稍微改變一下

-(void)method_one{
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
//創建多個串行隊列
        dispatch_async( dispatch_queue_create("fdd", DISPATCH_QUEUE_SERIAL), ^{
            NSLog(@"async--%@",[NSThread currentThread]);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);
}

執行結果:


屏幕快照 2017-11-23 上午10.19.39.png

分析:在for循環中,我們同時創建多個串行隊列,將異步任務分別添加到不同的隊列中,這個時候會創建10個串行隊列,開啟10條子線程,去異步的執行這些任務,所以任務的執行順序是無序的。

2.串行隊列 同步任務

-(void)method_2{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_SERIAL);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);
}

執行結果如下:


屏幕快照 2017-11-23 上午10.25.46.png

分析:創建了一個串行隊列,然后往串行隊列中添加了10個同步任務,從打印結果可以看出:先在主線程中執行打印start—,然后在主線程依次打印同步任務,最后在主線程打印end—。所以,主線程在執行dispatch_sync方法后,并沒有立刻返回,而是阻塞了當前線程,去等待dispatch_sync方法里面的block執行完畢,等到for循環里面所有同步任務執行完畢后,才返回去執行后面的end—的打印操作,所有的打印都是在主線程完成。

3.并行隊列 異步任務

-(void)method_3{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@ i = %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);
}

執行結果


屏幕快照 2017-11-23 上午10.30.52.png

分析:先在主線程中執行打印start—,然后將異步任務添加到并行隊列中(只是添加,并沒有立即執行),之后立刻返回,在主線程打印end—,最后才會從并行隊列中依次取出多個任務,并創建多個子線程來執行(至于創建幾個子線程由系統決定),由于每個任務執行時間不同,子線程獲得CPU的時間也不同,所以異步任務打印結果的順序也不同,而且每次打印的同步任務結果都不一樣。

4.并行隊列 同步任務

-(void)method_4{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);
}

執行結果


并行隊列 同步任務

分析:先在主線程中執行打印start—,由于是同步任務,所以不會創建子線程,任務會全部在主線程執行,在主線程依次打印同步任務,最后在主線程打印end—。所以,主線程在執行dispatch_sync方法后,并沒有立刻返回,而是阻塞了當前線程,去等待dispatch_sync方法里面的block執行完畢,等到for循環里面所有同步任務執行完畢后,才返回去執行后面的end—的打印操作,所有的打印都是在主線程完成。

5.全局隊列 同步任務

- (void)method_6{
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
  dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);  
}

執行結果:

屏幕快照 2017-11-23 上午10.45.07.png

分析:全局隊列也是并發隊列的一種,不用創建可以直接獲取。先在主線程中執行打印start—,由于是同步任務,所以不會創建子線程,任務會全部在主線程執行,在主線程依次打印同步任務,最后在主線程打印end—。所以,主線程在執行dispatch_sync方法后,并沒有立刻返回,而是阻塞了當前線程,去等待dispatch_sync方法里面的block執行完畢,等到for循環里面所有同步任務執行完畢后,才返回去執行后面的end—的打印操作,所有的打印都是在主線程完成。

5.全局隊列 異步任務


- (void)method_8{
    dispatch_queue_t q =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end--%@",[NSThread currentThread]);
}

執行結果


屏幕快照 2017-11-23 上午11.24.41.png

分析:先在主線程中執行打印start—,由于是同步任務,所以不會創建子線程,任務會全部在主線程執行,在主線程依次打印同步任務,最后在主線程打印end—。所以,主線程在執行dispatch_sync方法后,并沒有立刻返回,而是阻塞了當前線程,去等待dispatch_sync方法里面的block執行完畢,等到for循環里面所有同步任務執行完畢后,才返回去執行后面的end—的打印操作,所有的打印都是在主線程完成。

6.串行隊列中先異步再同步

-(void)method_9{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_SERIAL);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@ i = %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end1--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end2--%@",[NSThread currentThread]);
}

執行結果


屏幕快照 2017-11-23 下午1.29.24.png

分析:先在主線程中執行打印start—,然后將異步任務添加到串行隊列中(只是添加,并沒有立即執行),之后在主線程打印end1—,再然后往串行隊列里添加了1個同步任務,此時主線程堵塞,等待串行隊列里地同步任務執行完畢。此時創建一個子線程開始依次執行異步任務,異步任務結束后,在主線程中執行同步任務(上面的結論)。1個同步任務執行完后(此時串行隊列里沒有任務了),主線程返回,for循環繼續往串行隊列里添加1個同步任務,此時主線程繼續阻塞,等待串行隊列里同步任務執行完畢,此時主線程執行這個同步任務,執行完畢后,主線程返回繼續for循環……。等for循環結束后,最后在主線程執行打印end2—的操作。

7.串行隊列中先同步再異步

-(void)method_10{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_SERIAL);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end1--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end2--%@",[NSThread currentThread]);
}

執行結果


屏幕快照 2017-11-23 下午1.26.40.png

分析:先在主線程中執行打印start—,然后往串行隊列里添加了1個同步任務,此時主線程堵塞,等待串行隊列里地同步任務執行完畢。此時創建一個子線程開始依次執行串行隊列的任務,在主線程中執行同步任務(上面的結論)。1個同步任務執行完后(此時串行隊列里沒有任務了),主線程返回,for循環繼續往串行隊列里添加1個同步任務,此時主線程繼續阻塞,等待串行隊列里同步任務執行完畢,此時主線程執行這個同步任務,執行完畢后,主線程返回繼續for循環……。等for循環結束后,在主線程打印end1—,然后執行到dispatch_async往串行隊列里添加異步任務,并立即返回,for循環結束后,串行隊列里有10個異步任務,此時主線程繼續往下執行打印end2—的操作。之后開始創建子線程依次執行串行隊列里地異步任務。

8.并行隊列中先異步再同步

-(void)test7{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end1--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end2--%@",[NSThread currentThread]);
}

執行結果


屏幕快照 2017-11-23 下午1.34.15.png

分析:上面代碼創建了一個并行隊列,然后往并行隊列中添加了10個異步任務和10個同步任務,上面只是某一次的打印結果,每次的打印結果都不一樣,從打印結果可以看出:由于是并行隊列,會開啟多個子線程執行異步任務,所以異步任務的打印結果是無序的,而同步任務由于都是在主線程中執行,所有總體是有序的。而且同步與異步任務是交叉著執行完畢的。

9.并行隊列中先同步再異步

-(void)method_10{
    dispatch_queue_t q = dispatch_queue_create("FDD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"sync--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end1--%@",[NSThread currentThread]);
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"async--%@---%d",[NSThread currentThread],i);
        });
    }
    NSLog(@"end2--%@",[NSThread currentThread]);
}

執行結果

屏幕快照 2017-11-23 下午1.37.36.png

分析:每次的打印結果也不一定一樣,從打印結果可以看出:都是先有序的執行完同步任務,再無序的執行異步任務,原因在于先添加的同步任務,沒添加一個同步任務會堵塞主線程,等待同步任務執行完畢,所以會依次在主線程執行同步任務,for循環結束后,此時并行隊列里為空,之后再往并行隊列中添加了10個異步任務,此時沒有堵塞主線程,主線程一直往下執行打印end2—,此時開啟多個子線程來執行執行異步任務,所以執行完的順序是未知的。

總結: 1. GCD中的FIFO隊列稱為dispatch queue,它可以保證先進來的任務先得到執行(但不保證一定先執行結束)。
2.串行隊列 異步任務,會創建子線程,且只創建一個子線程,異步任務執行是有序的。
3.并行隊列 異步任務 創建子線程,且多個子線程,異步任務打印結果無序。
4.同步、異步決定是否創建子線程,同步任務不創建子線程,都是在主線程中執行,異步任務創建子線程。
5.串行、并行決定創建子線程的個數,串行創建一個子線程,并行創建多個子線程(具體幾個由系統決定)。

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

推薦閱讀更多精彩內容