多線程實(shí)現(xiàn)的幾種方案
| 技術(shù)方案 | 簡介 | 語言 | 線程生命周期 | 使用頻率 |
|---|---|---|---|---|
| pthread | 一套通用的多線程方案 適用于Linux、Unix、macOS等平臺 跨平臺/可移植,使用難度大 |
C | 程序員管理 | 機(jī)會不用 |
| NSThread | 蘋果封裝,更加面向?qū)ο?br>簡單使用,可直接操作線程對象 | OC | 程序員管理 | 偶爾使用 |
| GCD | 旨在替代NSThread多線程技術(shù) 充分利用設(shè)備的多核 |
C | 自動管理 | 經(jīng)常使用 |
| NSOperation | 基于GCD(底層GCD) 但比GCD多了一些更簡單實(shí)用的功能 使用更加面向?qū)ο?/td> | OC | 自動管理 | 經(jīng)常使用 |
GCD的簡介
GCD中有兩個很重要的概念: 任務(wù) 、隊列。隊列中存放的就是多個任務(wù),隊列的執(zhí)行,就是執(zhí)行其存放的各個任務(wù)。
任務(wù):即操作,說白了就是一段代碼塊,在GCD中就是一個block,所以添加任務(wù)十分方便。任務(wù)有同步(sync)和異步(async)的區(qū)分。主要的區(qū)別在于會不會阻塞當(dāng)前的線程。
-
同步任務(wù)(sync,即同步操作),它會阻塞當(dāng)前線程,直到block中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會繼續(xù)執(zhí)行。 -
異步任務(wù)(async,即異步操作),它不會阻塞當(dāng)前線程,當(dāng)前線程會直接往下執(zhí)行。
隊列:用于存放任務(wù),決定任務(wù)是順序執(zhí)行的還是并發(fā)執(zhí)行的。隊列對應(yīng)的有兩種:串行隊列、并行隊列。
-
串行隊列,根據(jù)定義,由于是隊列(queue),它遵從隊列的FIFO特性,即按照任務(wù)添加的順序存依次執(zhí)行。 -
并行隊列,放到并行隊列的任務(wù),GCD 也會 FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。這樣由于取的動作很快,忽略不計,看起來,所有的任務(wù)都是一起執(zhí)行的。不過需要注意,GCD 會根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會讓所有任務(wù)同時執(zhí)行。
線程(
thread)與隊列(queue)的關(guān)系:線程和隊列是兩個不同的概念。隊列是用來存放任務(wù)的,而線程是由系統(tǒng)去創(chuàng)建和管理,而且一個線程中可能有多個串行和并行隊列。
創(chuàng)建隊列
-
主隊列:這是一個特殊的
串行隊列。主隊列用于刷新UI,任何需要刷新UI的工作都是在主隊列中執(zhí)行,所以一般耗時的任務(wù)都是放到別的線程中執(zhí)行。
//獲取主隊列,即主線程
dispatch_queue_t queue = dispatch_get_main_queue();
- 手動創(chuàng)建隊列:
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
其中第一個參數(shù)是標(biāo)識符,用于DEBUG時便于知道是哪個唯一的隊列,可以為空。
第二個參數(shù)用來表示創(chuàng)建的隊列是串行隊列、還是并行隊列:
- DISPATCH_QUEUE_SERIAL或者NULL表示創(chuàng)建串行隊列;
- DISPATCH_QUEUE_CONCURRENT表示創(chuàng)建的是并行隊列。
- 全局并發(fā)隊列:系統(tǒng)提供的一個并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
創(chuàng)建任務(wù)
- 同步任務(wù):會阻塞當(dāng)前線程(
sync)
dispatch_sync(<#queue#>, ^{
//code here
NSLog(@"同步任務(wù)");
});
- 異步任務(wù):不會阻塞(
async)
dispatch_async(<#queue#>, ^{
//code here
NSLog(@"異步任務(wù)");
});
組合代碼測試
- 同步任務(wù) + 主隊列
代碼:
//主隊列的任務(wù)(A)
- (void)syncAndMainQueue {
//同步任務(wù) + 主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0 ; index < 10; index ++) {
//同步任務(wù)(B)
dispatch_sync(mainQueue, ^{
NSLog(@"index : %ld --- %@", index , [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打印信息:

結(jié)論:程序卡死,由于是同步任務(wù)(B),會阻塞當(dāng)前線程,等待加入到線程的同步任務(wù)執(zhí)行完成之后才會繼續(xù)當(dāng)前任務(wù),而由于主隊列是串行隊列,會按按順序(
FIFO)執(zhí)行隊列中添加的任務(wù),所以同步任何會等待正在執(zhí)行的任務(wù)執(zhí)行完畢,這樣就造成了相互等待,繼而造成死鎖。
- 異步任務(wù) + 主隊列
代碼:
- (void)asyncAndMainQueue {
//異步任務(wù) + 主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0 ; index < 10; index ++) {
dispatch_async(mainQueue, ^{
NSLog(@"index : %ld --- %@", index , [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打印信息:

結(jié)論:在主線程串行執(zhí)行。由于是異步任務(wù),不會阻塞當(dāng)前線程,由于主隊列是串行隊列,等待當(dāng)前任務(wù)執(zhí)行完成之后按順序執(zhí)行任務(wù)。
- 同步任務(wù) + 串行隊列
代碼:
- (void)syncAndSerialQueue {
//同步任務(wù) + 串行隊列
dispatch_queue_t serailQueue = dispatch_queue_create("serialQueue", NULL);
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0 ; index < 10; index ++) {
dispatch_sync(serailQueue, ^{
NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打?。?/p>

結(jié)論:沒有開啟新線程,在當(dāng)前線程執(zhí)行該同步任務(wù)。由于是同步任務(wù),會阻塞當(dāng)前線程,而隊列是串行隊列,會按順序執(zhí)行任務(wù),等到該隊列中的所有任務(wù)執(zhí)行完畢之后,當(dāng)前線程才會繼續(xù)執(zhí)行下去。
- 異步任務(wù) + 串行隊列
代碼:
- (void)asyncAndSerialQueue {
//異步任務(wù) + 串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0; index < 10; index ++) {
dispatch_async(serialQueue, ^{
NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打?。?/p>

結(jié)論:由于是異步任務(wù),不會阻塞當(dāng)前線程,開了新線程,由于是串行隊列,會按順序執(zhí)行任務(wù),所以只需開啟一條新的線程。
- 同步任務(wù) + 并行隊列
代碼:
- (void)syncAndConcurrent {
//同步任務(wù) + 并行隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0; index < 10; index ++) {
dispatch_sync(concurrentQueue, ^{
NSLog(@"index: %ld --- %@", index , [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打?。?/p>

結(jié)論:由于是同步任務(wù),會阻塞當(dāng)前線程,且不會開啟新線程,所以同步任務(wù)會在當(dāng)前線程執(zhí)行完畢,等到完畢之后,當(dāng)前線程執(zhí)行執(zhí)行。
- 異步任務(wù) + 并行隊列
- (void)asyncAndConcurrent {
//異步任務(wù) + 并行隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin --- %@", [NSThread currentThread]);
for (NSInteger index = 0; index < 10; index ++) {
dispatch_async(concurrentQueue, ^{
NSLog(@"index: %ld --- %@", index, [NSThread currentThread]);
});
}
NSLog(@"end --- %@", [NSThread currentThread]);
}
打印

結(jié)論:由于是異步任務(wù),不會阻塞當(dāng)前線程,同時會開啟新的線程,而且由于隊列是并行隊列,會開啟多條線程,并發(fā)的執(zhí)行這些異步任務(wù)。
總結(jié)
| 同步(sync) | 異步(async) | |
|---|---|---|
| 串行隊列 | 當(dāng)前線程,順序執(zhí)行 | 其他線程,順序執(zhí)行 |
| 并行隊列 | 當(dāng)前線程,順序執(zhí)行 | 開多個線程,并發(fā)執(zhí)行 |
| 主隊列 | 程序死鎖 | 主隊列,順序執(zhí)行 |
- 開不開線程,取決于執(zhí)行任務(wù)的函數(shù),同步不開,異步開。
- 開幾條線程,取決于隊列,串行開一條,并發(fā)開多條(異步)
- 主隊列: 專門用來在主線程上調(diào)度任務(wù)的"隊列",主隊列不能在其他線程中調(diào)度任務(wù)!