使用GCD前,先對隊列總結一下:
串行和并發(fā)
GCD中隊列分為兩種:串行隊列、并發(fā)隊列。
串行隊列:任務按在隊列里的先后順序執(zhí)行,先進先出(FIFO),一個任務執(zhí)行完成后,才會執(zhí)行下一個任務(實際實現(xiàn)是在一個線程中完成所有任務,這個線程可能是主線程,也可能是子線程)。
并發(fā)隊列:任務無序同時執(zhí)行(實際實現(xiàn)是在多個子線程中執(zhí)行)。
同步和異步
任務的執(zhí)行方式分為同步和異步兩種,同步和異步是以是否在當前線程執(zhí)行區(qū)分的。
同步執(zhí)行:任務都在當前線程中執(zhí)行,并且會阻塞當前線程。
異步執(zhí)行:任務會開辟新的線程,并在新的線程中執(zhí)行,不會阻塞當前線程。
組合四種隊列
兩種隊列和兩種執(zhí)行方式組合起來就形成了四種隊列:串行同步隊列、串行異步隊列、并發(fā)同步隊列、并發(fā)異步隊列。四種隊列區(qū)別如下(先貼結論,后面驗證):
串行同步隊列:任務都在當前線程執(zhí)行(同步),并且順序執(zhí)行(串行)
串行異步隊列:任務都在開辟的新的子線程中執(zhí)行(異步),并且順序執(zhí)行(串行)
并發(fā)同步隊列:任務都在當前線程執(zhí)行(同步),但是是順序執(zhí)行的(并發(fā)的特性并沒有體現(xiàn)出來)
并發(fā)異步隊列:任務在開辟的多個子線程中執(zhí)行(異步),并且是同時執(zhí)行的(并發(fā))
注:由上面的特性即可看出,我們平時對GCD使用最多的是并發(fā)異步隊列。
準備工作
研究隊列類型之間的區(qū)別前,先看一下GCD中生成隊列的方法:
方法一:dispatch_queue_create:可以生成串行和并發(fā)隊列
// 前兩個方法生成的是串行隊列;第三個生成的是并發(fā)隊列
dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_create("com.serial.queue", NULL);
dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
方法二:dispatch_get_global_queue:只能生成并發(fā)隊列
// 只能生成并發(fā)隊列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
驗證四種隊列
打印方法:
#pragma mark - private methods
- (void)p_printThread:(int)index {
NSLog(@"%@-------%d", [NSThread currentThread], index);
}
1.串行同步隊列
dispatch_queue_t queue = dispatch_queue_create("com.serial.queue", NULL);
dispatch_sync(queue, ^(){
[self p_printThread:1];
});
dispatch_sync(queue, ^(){
[self p_printThread:2];
});
dispatch_sync(queue, ^(){
[self p_printThread:3];
});
dispatch_sync(queue, ^(){
[self p_printThread:4];
});
打印結果:
<NSThread: 0x608000068380>{number = 1, name = main}-------1
<NSThread: 0x608000068380>{number = 1, name = main}-------2
<NSThread: 0x608000068380>{number = 1, name = main}-------3
<NSThread: 0x608000068380>{number = 1, name = main}-------4
結論:在串行同步隊列中,任務都在當前線程執(zhí)行(同步),并且順序執(zhí)行(串行)。
注:打印的結果都是主線程,是因為當前線程是主線程,這并不是說同步就是在主線程執(zhí)行的。
2.串行異步隊列
dispatch_queue_t queue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^(){
[self p_printThread:1];
});
dispatch_async(queue, ^(){
[self p_printThread:2];
});
dispatch_async(queue, ^(){
[self p_printThread:3];
});
dispatch_async(queue, ^(){
[self p_printThread:4];
});
打印結果:
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------1
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------2
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------3
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------4
結論:在串行異步隊列中,任務都在新開辟的子線程中執(zhí)行(異步),并且順序執(zhí)行(串行)。
3.并發(fā)同步隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^(){
[self p_printThread:1];
});
dispatch_sync(queue, ^(){
[self p_printThread:2];
});
dispatch_sync(queue, ^(){
[self p_printThread:3];
});
dispatch_sync(queue, ^(){
[self p_printThread:4];
});
打印結果:
<NSThread: 0x6080000790c0>{number = 1, name = main}-------1
<NSThread: 0x6080000790c0>{number = 1, name = main}-------2
<NSThread: 0x6080000790c0>{number = 1, name = main}-------3
<NSThread: 0x6080000790c0>{number = 1, name = main}-------4
結論:在并發(fā)同步隊列中,任務都在當前線程中執(zhí)行(同步),但是是順序執(zhí)行的(并發(fā)的特性并沒有體現(xiàn)出來,因為同步會阻塞當前線程)。
4.并發(fā)異步隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^(){
[self p_printThread:1];
});
dispatch_async(queue, ^(){
[self p_printThread:2];
});
dispatch_async(queue, ^(){
[self p_printThread:3];
});
dispatch_async(queue, ^(){
[self p_printThread:4];
});
打印結果:
<NSThread: 0x618000066700>{number = 4, name = (null)}-------2
<NSThread: 0x60000006cc80>{number = 6, name = (null)}-------4
<NSThread: 0x60000006ccc0>{number = 3, name = (null)}-------1
<NSThread: 0x6080000635c0>{number = 5, name = (null)}-------3
結論:在并發(fā)異步隊列中,任務在多個新開辟的子線程中執(zhí)行(異步),并且是同時無序地執(zhí)行的(并發(fā))
分析總結
由上面代碼示例可以看出,串行隊列就是在一個線程執(zhí)行的隊列:如果是當前線程,那就是串行同步隊列(因為同步機制會阻塞當前線程),如果是新開辟的子線程,就是串行異步隊列;并發(fā)隊列如果是在當前線程中執(zhí)行,就是并發(fā)同步隊列,在新開辟的多個子線程中執(zhí)行就是并發(fā)異步隊列。
綜上所述,我們平時使用最多的自然是并發(fā)異步隊列,比如開辟多個子線程下載圖片、文件等。