前言
對(duì)初學(xué)者來(lái)說(shuō),GCD似乎是一道邁不過(guò)去的坎,很多人在同步撒妈、異步恢暖、串行、并行和死鎖這幾個(gè)名詞的漩渦中漸漸放棄治療狰右。本文將使用圖文表并茂的方式給大家形象地解釋其中的原理和規(guī)律杰捂。
線程、任務(wù)和隊(duì)列的概念
異步棋蚌、同步 & 并行嫁佳、串行的特點(diǎn)
一條重要的準(zhǔn)則
一般來(lái)說(shuō),我們使用GCD的最大目的是在新的線程中同時(shí)執(zhí)行多個(gè)任務(wù)谷暮,這意味著我們需要兩項(xiàng)條件:
- 能開(kāi)啟新的線程
- 任務(wù)可以同時(shí)執(zhí)行
- 結(jié)合以上兩個(gè)條件蒿往,也就等價(jià)“開(kāi)啟新線程的能力 + 任務(wù)同步執(zhí)行的權(quán)利”,只有在滿足能力與權(quán)利這兩個(gè)條件的前提下湿弦,我們才可以在同時(shí)執(zhí)行多個(gè)任務(wù)瓤漏。
所有組合的特點(diǎn)
(一)異步執(zhí)行 + 并行隊(duì)列
實(shí)現(xiàn)代碼:
//異步執(zhí)行 + 并行隊(duì)列
- (void)asyncConcurrent{
//創(chuàng)建一個(gè)并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("標(biāo)識(shí)符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用異步函數(shù)封裝三個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
---end---
任務(wù)3---<NSThread: 0x600000070f00>{number = 5, name = (null)}
任務(wù)2---<NSThread: 0x600000070d80>{number = 4, name = (null)}
任務(wù)1---<NSThread: 0x608000074100>{number = 3, name = (null)}
解釋
- 異步執(zhí)行意味著
- 可以開(kāi)啟新的線程
- 任務(wù)可以先繞過(guò)不執(zhí)行,回頭再來(lái)執(zhí)行
- 并行隊(duì)列意味著
- 任務(wù)之間不需要排隊(duì)颊埃,且具有同時(shí)被執(zhí)行的“權(quán)利”
- 兩者組合后的結(jié)果
- 開(kāi)了三個(gè)新線程
- 函數(shù)在執(zhí)行時(shí)蔬充,先打印了start和end,再回頭執(zhí)行這三個(gè)任務(wù)
- 這三個(gè)任務(wù)是同時(shí)執(zhí)行的班利,沒(méi)有先后饥漫,所以打印結(jié)果是“任務(wù)3-->任務(wù)2-->任務(wù)1”
步驟圖
(二)異步執(zhí)行 + 串行隊(duì)列
實(shí)現(xiàn)代碼:
//異步執(zhí)行 + 串行隊(duì)列
- (void)asyncSerial{
//創(chuàng)建一個(gè)串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("標(biāo)識(shí)符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用異步函數(shù)封裝三個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
---end---
任務(wù)1---<NSThread: 0x608000078480>{number = 3, name = (null)}
任務(wù)2---<NSThread: 0x608000078480>{number = 3, name = (null)}
任務(wù)3---<NSThread: 0x608000078480>{number = 3, name = (null)}
解釋
- 異步執(zhí)行意味著
- 可以開(kāi)啟新的線程
- 任務(wù)可以先繞過(guò)不執(zhí)行,回頭再來(lái)執(zhí)行
- 串行隊(duì)列意味著
- 任務(wù)必須按添加進(jìn)隊(duì)列的順序挨個(gè)執(zhí)行
- 兩者組合后的結(jié)果
- 開(kāi)了一個(gè)新的子線程
- 函數(shù)在執(zhí)行時(shí)罗标,先打印了start和end庸队,再回頭執(zhí)行這三個(gè)任務(wù)
- 這三個(gè)任務(wù)是按順序執(zhí)行的,所以打印結(jié)果是“任務(wù)1-->任務(wù)2-->任務(wù)3”
步驟圖
(三)同步執(zhí)行 + 并行隊(duì)列
實(shí)現(xiàn)代碼:
//同步執(zhí)行 + 并行隊(duì)列
- (void)syncConcurrent{
//創(chuàng)建一個(gè)并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("標(biāo)識(shí)符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用同步函數(shù)封裝三個(gè)任務(wù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
任務(wù)1---<NSThread: 0x608000065400>{number = 1, name = main}
任務(wù)2---<NSThread: 0x608000065400>{number = 1, name = main}
任務(wù)3---<NSThread: 0x608000065400>{number = 1, name = main}
---end---
解釋
- 同步執(zhí)行執(zhí)行意味著
- 不能開(kāi)啟新的線程
- 任務(wù)創(chuàng)建后必須執(zhí)行完才能往下走
- 并行隊(duì)列意味著
- 任務(wù)之間不需要排隊(duì)馒稍,且具有同時(shí)被執(zhí)行的“”
- 兩者組合后的結(jié)果
- 所有任務(wù)都只能在主線程中執(zhí)行
- 函數(shù)在執(zhí)行時(shí)皿哨,必須按照代碼的書(shū)寫(xiě)順序一行一行地執(zhí)行完才能繼續(xù)
- 注意事項(xiàng)
- 在這里即便是并行隊(duì)列,任務(wù)可以同時(shí)執(zhí)行纽谒,但是由于只存在一個(gè)主線程证膨,所以沒(méi)法把任務(wù)分發(fā)到不同的線程去同步處理,其結(jié)果就是只能在主線程里按順序挨個(gè)挨個(gè)執(zhí)行了
步驟圖
(四)同步執(zhí)行+ 串行隊(duì)列
實(shí)現(xiàn)代碼:
- (void)syncSerial{
//創(chuàng)建一個(gè)串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("標(biāo)識(shí)符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用異步函數(shù)封裝三個(gè)任務(wù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
任務(wù)1---<NSThread: 0x608000065400>{number = 1, name = main}
任務(wù)2---<NSThread: 0x608000065400>{number = 1, name = main}
任務(wù)3---<NSThread: 0x608000065400>{number = 1, name = main}
---end---
解釋
- 這里的執(zhí)行原理和步驟圖跟“同步執(zhí)行+并發(fā)隊(duì)列”是一樣的鼓黔,只要是同步執(zhí)行就沒(méi)法開(kāi)啟新的線程央勒,所以多個(gè)任務(wù)之間也一樣只能按順序來(lái)執(zhí)行,
(五)異步執(zhí)行+主隊(duì)列
實(shí)現(xiàn)代碼:
- (void)asyncMain{
//獲取主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用異步函數(shù)封裝三個(gè)任務(wù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
---end---
任務(wù)1---<NSThread: 0x60800006ff40>{number = 1, name = main}
任務(wù)2---<NSThread: 0x60800006ff40>{number = 1, name = main}
任務(wù)3---<NSThread: 0x60800006ff40>{number = 1, name = main}
解釋
- 異步執(zhí)行意味著
- 可以開(kāi)啟新的線程
- 任務(wù)可以先繞過(guò)不執(zhí)行澳化,回頭再來(lái)執(zhí)行
- 主隊(duì)列跟串行隊(duì)列的區(qū)別
- 隊(duì)列中的任務(wù)一樣要按順序執(zhí)行
- 主隊(duì)列中的任務(wù)必須在主線程中執(zhí)行崔步,不允許在子線程中執(zhí)行
- 以上條件組合后得出結(jié)果:
- 所有任務(wù)都可以先跳過(guò),之后再來(lái)“按順序”執(zhí)行
步驟圖
(六)同步執(zhí)行+主隊(duì)列(死鎖)
實(shí)現(xiàn)代碼:
- (void)syncMain{
//獲取主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用同步函數(shù)封裝三個(gè)任務(wù)
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
打印結(jié)果:
---start---
解釋
- 主隊(duì)列中的任務(wù)必須按順序挨個(gè)執(zhí)行
- 任務(wù)1要等主線程有空的時(shí)候(即主隊(duì)列中的所有任務(wù)執(zhí)行完)才能執(zhí)行
- 主線程要執(zhí)行完“打印end”的任務(wù)后才有空
- “任務(wù)1”和“打印end”兩個(gè)任務(wù)互相等待缎谷,造成死鎖
步驟圖
寫(xiě)在結(jié)尾的話
以上就是我對(duì)GCD的基礎(chǔ)知識(shí)和幾種組合的理解井濒,如果覺(jué)得我的博客寫(xiě)得還可以,歡迎關(guān)注我的博客,本人將長(zhǎng)期為大家推出高質(zhì)量的技術(shù)博客瑞你。當(dāng)然酪惭,如果覺(jué)得我哪里理解有錯(cuò)的,也可以留下你的評(píng)論者甲。