看了兩遍 iOS 多線程:『GCD』詳盡總結(jié) ,這里我手打一遍加深下映象,僅供自己加深映象使用,具體參考上面的鏈接
1.GCD簡(jiǎn)介
Grand Central Dispatch(GCD)是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對(duì)稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并發(fā)任務(wù)。
為什么要用GCD呢(面試也可能問(wèn)用GCD的好處)
- 可用于多核的并行運(yùn)算
- 會(huì)自動(dòng)利用更多的CPU內(nèi)核
- 會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理的代碼
2.GCD任務(wù)和隊(duì)列
任務(wù): 就是執(zhí)行操作的意思,換句話說(shuō)就是你在線程中執(zhí)行的代碼塊。在GCD中是放在block中的。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行(sync)和 異步執(zhí)行(async)。兩者的主要區(qū)別是:是否等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束,以及是否具備開啟新線程的能力。
- 同步執(zhí)行(sync):
- 同步添加任務(wù)到指定的任務(wù)隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行。
2.只能在當(dāng)前已有線程中執(zhí)行任務(wù),不具備開啟新線程的能力。
- 同步添加任務(wù)到指定的任務(wù)隊(duì)列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行。
- 異步執(zhí)行(async)
1. 異步添加任務(wù)到指定的隊(duì)列中,它不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)。
2.可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力。
注意:異步執(zhí)行(async) 雖然具有開啟新線程的能力,但是并不一定開啟新線程。這根任務(wù)所指定的隊(duì)列類型有關(guān)
隊(duì)列(Dispatch Queue):這里的隊(duì)列指執(zhí)行任務(wù)的等待隊(duì)列,既用來(lái)存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表,采用FIFO(先進(jìn)先出)的原則,既新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。

在GCD中有兩種隊(duì)列:串行隊(duì)列和并發(fā)隊(duì)列。兩者都符合FIFO(先進(jìn)先出)的原則。兩者的主要區(qū)別是:執(zhí)行順序不同,以及開啟線程數(shù)不同。
- 串行隊(duì)列(Serial Dispatch Queue):
每次只有一個(gè)任務(wù)被執(zhí)行。讓任務(wù)一個(gè)接著一個(gè)執(zhí)行。(只開啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)) - 并發(fā)隊(duì)列(Concurrent Dispatch Queue):
可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行。(可以開啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))
注意:并發(fā)隊(duì)列的并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效


3.GCD的使用步驟
GCD的使用步驟
1.創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列或并發(fā)隊(duì)列)
2.將任務(wù)追加到任務(wù)的等待隊(duì)列中,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或者異步執(zhí)行)
3.1 隊(duì)列的創(chuàng)建方法/獲取方法
- 可以使用 dispatch_queue_create 來(lái)創(chuàng)建隊(duì)列,需要傳入兩個(gè)參數(shù),第一個(gè)參數(shù)標(biāo)識(shí)隊(duì)列的唯一標(biāo)識(shí)符,用于DEBUG,可為空,Dispatch Queue的名稱推薦使用應(yīng)用程序ID這種逆序全程域名; 第二個(gè)參數(shù)用來(lái)識(shí)別是串行隊(duì)列還是并發(fā)隊(duì)列。 DISPATCH_QUEUE_SERIAL表示串行隊(duì)列,DISPATCH_QUEUE_CONCURRENT表示并發(fā)隊(duì)列。
// 串行隊(duì)列創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("net.ceshi.test", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("net.ceshi.test", DISPATCH_QUEUE_CONCURRENT);
- 對(duì)于串行隊(duì)列,GCD提供了的一種特殊的串行隊(duì)列:主隊(duì)列(Main Dispatch Queue)
1.所有放在主隊(duì)列中的任務(wù),都會(huì)放到主線程中執(zhí)行。
2.可以使用dispatch_get_main_queue()獲得主隊(duì)列。 - 對(duì)于并發(fā)隊(duì)列,GCD默認(rèn)提供了全局并發(fā)隊(duì)列 (Global Dispatch Queue)。
可以使用dispatch_get_global_queue來(lái)獲取。需要傳入兩個(gè)參數(shù)。第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級(jí),一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個(gè)參數(shù)暫時(shí)沒(méi)用,用0即可。
3.2 任務(wù)的創(chuàng)建方法
GCD提供了同步執(zhí)行任務(wù)的創(chuàng)建方法dispatch_sync和異步執(zhí)行任務(wù)創(chuàng)建方法dispatch_async。
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
// 這里放同步執(zhí)行任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
// 這里放異步執(zhí)行任務(wù)代碼
});
實(shí)際上剛才還說(shuō)了兩種特殊隊(duì)列:全局并發(fā)隊(duì)列、主隊(duì)列。全局并發(fā)隊(duì)列可以作為普通并發(fā)隊(duì)列來(lái)使用。但是主隊(duì)列比較特殊,所以又多了兩種組合方式。這樣就有六種不用的組合方式
同步執(zhí)行 + 并發(fā)隊(duì)列
異步執(zhí)行 + 并發(fā)隊(duì)列
同步執(zhí)行 + 串行隊(duì)列
異步執(zhí)行 + 串行隊(duì)列
同步執(zhí)行 + 主隊(duì)列
異步執(zhí)行 + 主隊(duì)列
| 區(qū)別 | 并發(fā)隊(duì)列 | 串行隊(duì)列 | 主隊(duì)列 |
|---|---|---|---|
| 同步 | 沒(méi)有開啟新線程,串行執(zhí)行任務(wù) | 沒(méi)有開啟新線程,串行執(zhí)行任務(wù) | 主線程調(diào)用:死鎖卡住不執(zhí)行。其他線程調(diào)用:沒(méi)有開啟新線程,串行執(zhí)行任務(wù) |
| 異步 | 有開啟新線程,并發(fā)執(zhí)行任務(wù) | 有開啟新線程(1條),串行執(zhí)行任務(wù) | 沒(méi)有開啟新線程,串行執(zhí)行任務(wù) |
4.GCD的基本使用
4.1 同步執(zhí)行+并發(fā)隊(duì)列
- 在當(dāng)前線程中執(zhí)行任務(wù),不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)。
/**
同步執(zhí)行 + 并發(fā)隊(duì)列
特點(diǎn) 在當(dāng)前線程中執(zhí)行任務(wù)不會(huì)開啟新線程,任務(wù)順序執(zhí)行
*/
- (void)syncConcurrent{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncConcurrent---end");
}
/*
打印結(jié)果
currentThread---<NSThread: 0x600001aa53c0>{number = 1, name = main}
asyncConcurrent---begin
1---<NSThread: 0x600001aa53c0>{number = 1, name = main}
2---<NSThread: 0x600001aa53c0>{number = 1, name = main}
2---<NSThread: 0x600001aa53c0>{number = 1, name = main}
syncConcurrent---end
*/
總結(jié)同步執(zhí)行+并發(fā)隊(duì)列:
- 所有任務(wù)都是在當(dāng)前線程(主線程中執(zhí)行),沒(méi)有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)
- 所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間執(zhí)行的(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。
- 任務(wù)按順序執(zhí)行。
原文 :任務(wù)按順序執(zhí)行的。按順序執(zhí)行的原因:雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)。但是因?yàn)楸旧聿荒軇?chuàng)建新線程,只有當(dāng)前線程這一個(gè)線程(同步任務(wù)不具備開啟新線程的能力),所以也就不存在并發(fā)。而且當(dāng)前線程只有等待當(dāng)前隊(duì)列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后,才能繼續(xù)接著執(zhí)行下面的操作(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。所以任務(wù)只能一個(gè)接一個(gè)按順序執(zhí)行,不能同時(shí)被執(zhí)行。
作者:行走少年郎
鏈接:http://www.itdecent.cn/p/2d57c72016c6
來(lái)源:簡(jiǎn)書
簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。
上面這段話 我自己理解的意思是 并發(fā)隊(duì)列調(diào)度任務(wù)的時(shí)候,如果是同步任務(wù)則只會(huì)在當(dāng)前線程執(zhí)行,并不會(huì)開啟新線程。(其實(shí)并發(fā)是需要調(diào)度其他線程來(lái)執(zhí)行任務(wù)的) 一個(gè)線程同一時(shí)間只能處理一個(gè)任務(wù),所以任務(wù)只能一個(gè)個(gè)執(zhí)行。
4.2 異步執(zhí)行 + 并發(fā)隊(duì)列
- 可以開啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行
- (void)asyncConcurrent{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncConcurrent---end");
/*
打印結(jié)果
currentThread---<NSThread: 0x6000020b2900>{number = 1, name = main}
asyncConcurrent---begin
asyncConcurrent---end
1---<NSThread: 0x6000020c9bc0>{number = 3, name = (null)}
2---<NSThread: 0x6000020e3b00>{number = 4, name = (null)}
2---<NSThread: 0x6000020e3b00>{number = 4, name = (null)}
1---<NSThread: 0x6000020c9bc0>{number = 3, name = (null)}
*/
}
總結(jié)異步執(zhí)行+并發(fā)隊(duì)列:
- 除了當(dāng)前線程(主線程),系統(tǒng)有開啟了3個(gè)線程,并且任務(wù)是交替同時(shí)執(zhí)行的。(異步執(zhí)行具備開啟線程的能力,并發(fā)隊(duì)列可以開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù))
- 所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才執(zhí)行的。說(shuō)明當(dāng)前線程沒(méi)有等待異步任務(wù)執(zhí)行,而是直接開啟新線程執(zhí)行任務(wù)。
4.3 同步執(zhí)行+串行隊(duì)列
- 不會(huì)開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),在執(zhí)行下一個(gè)任務(wù)。
- (void)syncSerial{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncSerial---end");
/*
打印結(jié)果
currentThread---<NSThread: 0x6000020b2900>{number = 1, name = main}
syncSerial---begin
1---<NSThread: 0x6000020b2900>{number = 1, name = main}
1---<NSThread: 0x6000020b2900>{number = 1, name = main}
2---<NSThread: 0x6000020b2900>{number = 1, name = main}
2---<NSThread: 0x6000020b2900>{number = 1, name = main}
syncSerial---end
*/
}
總結(jié):同步執(zhí)行 + 串行隊(duì)列
- 所有任務(wù)都在主線程中執(zhí)行,沒(méi)有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)。
- 所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)(需要等待)。
4.4異步執(zhí)行 + 串行隊(duì)列
- 會(huì)開啟新線程,但是因?yàn)槿蝿?wù)是串行的,任務(wù)順序執(zhí)行
- (void)asyncSerial{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncSerial---end");
/*
currentThread---<NSThread: 0x6000020b2900>{number = 1, name = main}
asyncSerial---begin
asyncSerial---end
1---<NSThread: 0x6000018dc000>{number = 3, name = (null)}
1---<NSThread: 0x6000018dc000>{number = 3, name = (null)}
2---<NSThread: 0x6000018dc000>{number = 3, name = (null)}
2---<NSThread: 0x6000018dc000>{number = 3, name = (null)}
*/
}
總結(jié):異步執(zhí)行 + 串行隊(duì)列
- 開啟了一條新線程(異步執(zhí)行具備開啟新線程的能力,串行隊(duì)列只開啟一條線程)。
- 所有任務(wù)是在打印syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的(異步執(zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù))。
- 任務(wù)按順序執(zhí)行
4.5 同步執(zhí)行 + 主隊(duì)列
- 主隊(duì)列:GCD自帶的一種特殊的串行隊(duì)列
同步執(zhí)行 + 主隊(duì)列在不同線程中調(diào)用結(jié)果也是不一樣,在主線程中調(diào)用會(huì)出現(xiàn)死鎖。在其他線程中則不會(huì)
4.5.1 在主線程中調(diào)用 同步執(zhí)行+ 主隊(duì)列
- 相互等待 卡死 崩潰
- (void)syncMain{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"syncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:1]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncMain---end");
/*
c輸出結(jié)果
currentThread---<NSThread: 0x600003d72880>{number = 1, name = main}
syncMain---begin
--- 崩潰
*/
}
總結(jié):同步執(zhí)行 + 主隊(duì)列
- 在主線程中使用同步執(zhí)行 + 主隊(duì)列,追加到主線程的任務(wù)1 不執(zhí)行 直接崩潰,end也沒(méi)打印。
這是因?yàn)槲覀冊(cè)僦骶€程中執(zhí)行syncMain方法,相當(dāng)于吧syncMain任務(wù)放到了主線程的隊(duì)列中。而同步執(zhí)行會(huì)等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢,才會(huì)接著執(zhí)行。那么當(dāng)我們把任務(wù)1 追加到主隊(duì)列中,任務(wù)1就在等主線程處理完syncMain任務(wù)。而syncMain任務(wù)需要等待任務(wù)1 執(zhí)行完畢,才能接著執(zhí)行。
要是如果不在主線程中調(diào)用,而在其他線程中調(diào)用會(huì)如何呢?
4.5.2 在其他線程中調(diào)用 同步執(zhí)行 + 主隊(duì)列
// 使用 NSThread 的 detachNewThreadSelector 方法會(huì)創(chuàng)建線程,并自動(dòng)啟動(dòng)線程執(zhí)行
selector 任務(wù)
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
/*
打印結(jié)果
currentThread---<NSThread: 0x600002205a80>{number = 3, name = (null)}
syncMain---begin
1---<NSThread: 0x60000226a940>{number = 1, name = main}
1---<NSThread: 0x60000226a940>{number = 1, name = main}
syncMain---end
*/
總結(jié):在其他線程中使用同步執(zhí)行 + 主隊(duì)列
- 所有任務(wù)都是在主線程(非當(dāng)前線程)中執(zhí)行的,沒(méi)有開啟新的線程(所有放在主隊(duì)列中的任務(wù),都會(huì)放到主線程中執(zhí)行)。
- 所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間執(zhí)行(同步任務(wù)需要等待隊(duì)列的任務(wù)執(zhí)行結(jié)束)。
- 任務(wù)是按順序執(zhí)行的(主隊(duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。
為什么現(xiàn)在就不會(huì)卡主了呢?
因?yàn)閟yncMain 任務(wù)放到了其他線程里,而任務(wù)1追加到主隊(duì)列中,這三個(gè)任務(wù)都會(huì)在主線程中執(zhí)行。syncMain 任務(wù)在其他線程中執(zhí)行到追加任務(wù)1到主隊(duì)列中時(shí),因?yàn)橹麝?duì)列現(xiàn)在沒(méi)有正在執(zhí)行的任務(wù),所以會(huì)直接執(zhí)行主隊(duì)列的任務(wù)1,等任務(wù)1執(zhí)行完畢,再接著執(zhí)行其他任務(wù)。所以這里不會(huì)卡主線程
4.6 異步執(zhí)行 + 主隊(duì)列
- 只在主線程中執(zhí)行任務(wù),任務(wù)順序執(zhí)行
/**
* 異步執(zhí)行 + 主隊(duì)列
* 特點(diǎn):只在主線程中執(zhí)行任務(wù),執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
*/
- (void)asyncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"asyncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncMain---end");
/*
currentThread---<NSThread: 0x6000028d6880>{number = 1, name = main}
asyncMain---begin
asyncMain---end
1---<NSThread: 0x6000028d6880>{number = 1, name = main}
1---<NSThread: 0x6000028d6880>{number = 1, name = main}
2---<NSThread: 0x6000028d6880>{number = 1, name = main}
2---<NSThread: 0x6000028d6880>{number = 1, name = main}
3---<NSThread: 0x6000028d6880>{number = 1, name = main}
3---<NSThread: 0x6000028d6880>{number = 1, name = main}
*/
}
總結(jié):異步執(zhí)行 + 主隊(duì)列
- 所有任務(wù)都是在當(dāng)前線程(主線程)中執(zhí)行的。并沒(méi)有開啟新的線程(雖然異步執(zhí)行具備開啟線程的能力,但因?yàn)槭侵麝?duì)列,所以所有任務(wù)都在主線程中)
- 所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的(異步執(zhí)行不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù))。
- 任務(wù)是按順序執(zhí)行的(因?yàn)橹麝?duì)列是串行隊(duì)列,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)。
GCD線程間的通信
在iOS開發(fā)過(guò)程中,我們一般在主線程里邊進(jìn)行UI刷新,例如:點(diǎn)擊、滾動(dòng)、拖拽等事件。我們通常把一些耗時(shí)的操作放在其他線程,比如說(shuō)圖片下載、文件上傳等耗時(shí)操作。而當(dāng)我們有時(shí)候在其他線程完成了耗時(shí)操作時(shí),需要回到主線程,那么就用到了線程之間的通訊。
/**
* 線程間通信
*/
- (void)communication {
// 獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 異步追加任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
// 回到主線程
dispatch_async(mainQueue, ^{
// 追加在主線程中執(zhí)行的任務(wù)
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
});
}
輸出結(jié)果:
2018-02-23 20:47:03.462394+0800 YSC-GCD-demo[20154:5053282] 1---<NSThread: 0x600000271940>{number = 3, name = (null)}
2018-02-23 20:47:05.465912+0800 YSC-GCD-demo[20154:5053282] 1---<NSThread: 0x600000271940>{number = 3, name = (null)}
2018-02-23 20:47:07.466657+0800 YSC-GCD-demo[20154:5052953] 2---<NSThread: 0x60000007bf80>{number = 1, name = main}
作者:行走少年郎
鏈接:http://www.itdecent.cn/p/2d57c72016c6
6 GCD的其他方法
6.1 GCD 柵欄方法:dispatch_barrier_async
-
有時(shí)我們需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開啟執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的方法將兩組異步執(zhí)行的操作組給分割起來(lái),當(dāng)然這里的操作組里可以包含一個(gè)或者多個(gè)任務(wù)。這就需要用到** dispatch_barrier_async**方法在兩個(gè)操作組間形成柵欄。
image
- (void)barrier{
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"%d---%@",i,[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 2; i < 4; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"%d---%@",i,[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_barrier_async(queue, ^{
// 追加任務(wù) barrier
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 4; i < 6; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"%d---%@",i,[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 6; i < 8; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"%d---%@",i,[NSThread currentThread]); // 打印當(dāng)前線程
}
});
}
打印結(jié)果
2019-07-03 21:24:58.105410+0800 二叉樹練習(xí)[34879:618905] 0---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:24:58.105408+0800 二叉樹練習(xí)[34879:618908] 2---<NSThread: 0x600003efb5c0>{number = 4, name = (null)}
2019-07-03 21:25:00.110444+0800 二叉樹練習(xí)[34879:618908] 3---<NSThread: 0x600003efb5c0>{number = 4, name = (null)}
2019-07-03 21:25:00.110444+0800 二叉樹練習(xí)[34879:618905] 1---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:25:02.113756+0800 二叉樹練習(xí)[34879:618905] barrier---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:25:04.118678+0800 二叉樹練習(xí)[34879:618905] barrier---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:25:06.123982+0800 二叉樹練習(xí)[34879:618908] 6---<NSThread: 0x600003efb5c0>{number = 4, name = (null)}
2019-07-03 21:25:06.123982+0800 二叉樹練習(xí)[34879:618905] 4---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:25:08.128269+0800 二叉樹練習(xí)[34879:618905] 5---<NSThread: 0x600003ed4200>{number = 3, name = (null)}
2019-07-03 21:25:08.128295+0800 二叉樹練習(xí)[34879:618908] 7---<NSThread: 0x600003efb5c0>{number = 4, name = (null)}
- 總結(jié)
- 柵欄方法的執(zhí)行順序?yàn)? 柵欄方法之前添加的任務(wù)組 --> 柵欄方法的任務(wù) -->柵欄方法之后添加的任務(wù)組
6.2 GCD 延時(shí)執(zhí)行方法:dispatch_after (常用的不抄了)
6.3 GCD 一次性代碼(只執(zhí)行一次):dispatch_once (常用的不抄了)
6.4 GCD 快速迭代方法:dispatch_apply
- 通常我們會(huì)用for循環(huán)遍歷,但是GCD給我們提供了快速迭代的函數(shù)dispatch_apply、它會(huì)按照我們指定的次數(shù)將指定的任務(wù)追加到指定的隊(duì)列中,并等待全部隊(duì)列執(zhí)行結(jié)束。
- 如果是在串行隊(duì)列中使用dispatch_apply,會(huì)和for循環(huán)一樣,按順序同步執(zhí)行,這樣就體現(xiàn)不出快速迭代的意義了。我們可以利用并發(fā)隊(duì)列進(jìn)行異步執(zhí)行。比如遍歷0--5 這6個(gè)數(shù)字。for 循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply 可以 在多個(gè)線程中同時(shí)(異步)遍歷多個(gè)數(shù)字。
還有一點(diǎn),無(wú)論是在串行隊(duì)列,還是并發(fā)隊(duì)列中,dispatch_apply 都會(huì)等待全部任務(wù)執(zhí)行完畢,這點(diǎn)就像是同步操作,也像是隊(duì)列組中的 dispatch_group_wait方法。
/**
* 快速迭代方法 dispatch_apply
*/
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
}
因?yàn)槭窃诓l(fā)隊(duì)列中異步執(zhí)行任務(wù),所以各個(gè)任務(wù)的執(zhí)行時(shí)間長(zhǎng)短不定,最后結(jié)束順序也不定。但是apply---end一定在最后執(zhí)行。這是因?yàn)閐ispatch_apply函數(shù)會(huì)等待全部任務(wù)執(zhí)行完畢。
6.5 GCD隊(duì)列組:dispatch_group
有時(shí)候我們會(huì)有這樣的需求:分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到GCD的隊(duì)列組。
- 調(diào)用隊(duì)列組的dispatch_group_async 先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中,或者使用隊(duì)列組的dispatch_group_enter、dispatch_group_leave 組合 來(lái)實(shí)現(xiàn)dispatch_group_async。
- 調(diào)用隊(duì)列組的dispatch_group_notify 回到指定線程執(zhí)行任務(wù)?;蛘呤褂胐ispatch_group_wait 回到當(dāng)前線程繼續(xù)向下執(zhí)行(會(huì)阻塞當(dāng)前線程)
6.5.1 dispatch_group_notify
- 監(jiān)聽group中任務(wù)的完成狀態(tài),當(dāng)所有的任務(wù)都執(zhí)行完成后,追加任務(wù)到group中,并執(zhí)行任務(wù)。
- (void)groupNotify{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步任務(wù)1、任務(wù)2都執(zhí)行完畢后,回到主線程執(zhí)行下邊任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
}
/*
打印結(jié)果
currentThread---<NSThread: 0x600002722940>{number = 1, name = main}
group---begin
2---<NSThread: 0x600002750bc0>{number = 4, name = (null)}
1---<NSThread: 0x600002758980>{number = 3, name = (null)}
1---<NSThread: 0x600002758980>{number = 3, name = (null)}
2---<NSThread: 0x600002750bc0>{number = 4, name = (null)}
3---<NSThread: 0x600002722940>{number = 1, name = main}
3---<NSThread: 0x600002722940>{number = 1, name = main}
group---end
*/
總結(jié):當(dāng)所有隊(duì)列組中的任務(wù)完成之后,才執(zhí)行dispatch_group_notify block中的任務(wù)。
6.5.2 dispatch_group_wait
- 暫停當(dāng)前線程(阻塞當(dāng)前線程),等待指定的group中的任務(wù)執(zhí)行完成后,才會(huì)往下繼續(xù)執(zhí)行。
6.5.3 dispatch_group_enter、dispatch_group_leave
- dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)+1
- dispatch_group_leave 標(biāo)志著一個(gè)任務(wù)離開了 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)-1。
- 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會(huì)使dispatch_group_wait解除阻塞,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)。
原文
/**
* 隊(duì)列組 dispatch_group_enter、dispatch_group_leave
*/
- (void)groupEnterAndLeave
{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程.
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
// // 等待上面的任務(wù)全部完成后,會(huì)往下繼續(xù)執(zhí)行(會(huì)阻塞當(dāng)前線程)
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//
// NSLog(@"group---end");
}
輸出結(jié)果:
2018-02-23 22:14:17.997667+0800 YSC-GCD-demo[20592:5214830] currentThread---<NSThread: 0x604000066600>{number = 1, name = main}
2018-02-23 22:14:17.997839+0800 YSC-GCD-demo[20592:5214830] group---begin
2018-02-23 22:14:20.000298+0800 YSC-GCD-demo[20592:5215094] 1---<NSThread: 0x600000277c80>{number = 4, name = (null)}
2018-02-23 22:14:20.000305+0800 YSC-GCD-demo[20592:5215095] 2---<NSThread: 0x600000277c40>{number = 3, name = (null)}
2018-02-23 22:14:22.001323+0800 YSC-GCD-demo[20592:5215094] 1---<NSThread: 0x600000277c80>{number = 4, name = (null)}
2018-02-23 22:14:22.001339+0800 YSC-GCD-demo[20592:5215095] 2---<NSThread: 0x600000277c40>{number = 3, name = (null)}
2018-02-23 22:14:24.002321+0800 YSC-GCD-demo[20592:5214830] 3---<NSThread: 0x604000066600>{number = 1, name = main}
2018-02-23 22:14:26.002852+0800 YSC-GCD-demo[20592:5214830] 3---<NSThread: 0x604000066600>{number = 1, name = main}
2018-02-23 22:14:26.003116+0800 YSC-GCD-demo[20592:5214830] group---end
從dispatch_group_enter、dispatch_group_leave相關(guān)代碼運(yùn)行結(jié)果中可以看出:當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行 dispatch_group_notify 中的任務(wù)。這里的dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。
我們自己在用隊(duì)列組的情況通常是 發(fā)起多個(gè)異步的網(wǎng)絡(luò)請(qǐng)求,等請(qǐng)求全部完成后刷新UI,這個(gè)時(shí)候?yàn)榱诵?保證等多個(gè)異步請(qǐng)求完成后再刷新UI 經(jīng)常會(huì)用dispatch_group_enter、dispatch_group_leave組合,如果只用dispatch_group_async則達(dá)不到這種效果。
/*
如果去掉ispatch_group_enter、dispatch_group_leave組合,則dispatch_group_async就不好使了。
*/
- (void)groupNotify{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步任務(wù)1、任務(wù)2都執(zhí)行完畢后,回到主線程執(zhí)行下邊任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
}
6.6 GCD 信號(hào)量:dispatch_semaphore
GCD中的信號(hào)量是指dispatch semaphore 是持有計(jì)數(shù)的信號(hào)。類似于過(guò)高速路收費(fèi)站的欄桿。可以通過(guò)時(shí),打開欄桿,不可以通過(guò)時(shí),關(guān)閉欄桿。在dispatch semaphore 中,使用計(jì)數(shù)來(lái)完成這個(gè)功能,計(jì)數(shù)小于0時(shí)等待,不可通過(guò)。計(jì)數(shù)為0或大于0時(shí),計(jì)數(shù)減1且不等待,可通過(guò)。
dispatch_semaphore 提供了三個(gè)函數(shù)。
- dispatch_semaphore_create :創(chuàng)建一個(gè)semaphore 并初始化信號(hào)的總量
- dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào),讓信號(hào)總量加1
- dispatch_semaphore_wait:可以使總信號(hào)量減一 信號(hào)總量小于0時(shí)就會(huì)一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
dispatch_semaphore_wait
// 等待降低信號(hào)量,接收一個(gè)信號(hào)和時(shí)間值(多為DISPATCH_TIME_FOREVER)
// 若信號(hào)的信號(hào)量為0,則會(huì)阻塞當(dāng)前線程,直到信號(hào)量大于0或者經(jīng)過(guò)輸入的時(shí)間值;
// 若信號(hào)量大于0,則會(huì)使信號(hào)量減1并返回,程序繼續(xù)住下執(zhí)行
信號(hào)量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞),又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號(hào)量。
Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:
- 保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
- 保證線程安全,為線程加鎖
6.6.1 Dispatch Semaphore 線程同步
我們?cè)陂_發(fā)中會(huì)遇到這樣的需求:異步執(zhí)行耗時(shí)任務(wù),并使用異步執(zhí)行的結(jié)果進(jìn)行一些額外的操作。換句話說(shuō),相當(dāng)于,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。比如說(shuō):AFNetworking 中 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法。通過(guò)引入信號(hào)量的方式,等待異步執(zhí)行任務(wù)結(jié)果,獲取到 tasks,然后再返回該 tasks
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
下面我們來(lái)利用Dispatch Semaphore 實(shí)現(xiàn)線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
- (void)semaphoreSync{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int number = 0;
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
number = 100;
dispatch_semaphore_signal(semaphore);
});
NSLog(@"semaphore--number = %d",number);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %d",number);
/*
打印結(jié)果:
currentThread---<NSThread: 0x600002452940>{number = 1, name = main}
semaphore---begin
semaphore---end,number = 0
1---<NSThread: 0x600002434f40>{number = 3, name = (null)}
semaphore---end,number = 100
*/
}
總結(jié):
- 打印結(jié)果 semaphore---end,number = 0 證明 異步執(zhí)行沒(méi)有等待,semaphore---end,number = 100 證明dispatch_semaphore_wait 是在等待 異步任務(wù)的。
任務(wù)執(zhí)行順序如下:
- semaphore 初始創(chuàng)建時(shí)計(jì)數(shù)為 0。
2.異步執(zhí)行將任務(wù) 1 追加到隊(duì)列之后,不做等待,接著執(zhí)行
3.打印 semaphore---end,number = 0
4.dispatch_semaphore_wait方法,semaphore 減 1,此時(shí) semaphore == -1,當(dāng)前線程進(jìn)入等待狀態(tài)。
5.然后,異步任務(wù) 1 開始執(zhí)行。任務(wù)1執(zhí)行到dispatch_semaphore_signal之后,總信號(hào)量加1,此時(shí) semaphore == 0,正在被阻塞的線程(主線程)恢復(fù)繼續(xù)執(zhí)行。
6.最后打印semaphore---end,number = 100
這樣就實(shí)現(xiàn)了線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。
6.6.2 Dispatch Semaphore 線程安全和線程同步(為線程加鎖)
線程安全: 如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期是一樣的,就是線程安全的。
若每個(gè)線程中對(duì)全局變量。靜態(tài)變量只有讀操作,而無(wú)寫操作,一般來(lái)說(shuō)這個(gè)全局變量是線程安全的。若有多個(gè)線程通知執(zhí)行寫操作(更改變量),一般都需要考慮線程同步,否則的話就可能影響線程安全。
線程同步:可理解為線程A和線程B一塊配合,A執(zhí)行到一定程度時(shí) 要依靠線程B的某個(gè)結(jié)果,于是停下來(lái),示意B運(yùn)行,B去執(zhí)行,再將接活給A,A繼續(xù)執(zhí)行。
模擬火車票售賣,實(shí)現(xiàn)線程安全和解決線程同步問(wèn)題。
- 場(chǎng)景:總共有50張火車票,有兩個(gè)售賣火車票的窗口,一個(gè)是北京火車票售賣窗口,另一個(gè)是上?;疖嚻笔圪u窗口。兩個(gè)窗口同時(shí)售賣火車票,賣完為止。
6.6.2.1 非線程安全(不使用semaphore)
先看看不考慮線程安全的代碼:
/**
* 非線程安全:不使用 semaphore
* 初始化火車票數(shù)量、賣票窗口(非線程安全)、并開始賣票
*/
- (void)initTicketStatusNotSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"semaphore---begin");
self.ticketSurplusCount = 50;
// queue1 代表北京火車票售賣窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上?;疖嚻笔圪u窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketNotSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketNotSafe];
});
}
/**
* 售賣火車票(非線程安全)
*/
- (void)saleTicketNotSafe {
while (1) {
if (self.ticketSurplusCount > 0) { //如果還有票,繼續(xù)售賣
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else { //如果已賣完,關(guān)閉售票窗口
NSLog(@"所有火車票均已售完");
break;
}
}
}
打印結(jié)果
剩余票數(shù):8 窗口:<NSThread: 0x6000002b7040>{number = 4, name = (null)}
剩余票數(shù):8 窗口:<NSThread: 0x600000295c40>{number = 3, name = (null)}
剩余票數(shù):7 窗口:<NSThread: 0x6000002b7040>{number = 4, name = (null)}
剩余票數(shù):6 窗口:<NSThread: 0x600000295c40>{number = 3, name = (null)}
剩余票數(shù):4 窗口:<NSThread: 0x6000002b7040>{number = 4, name = (null)}
剩余票數(shù):5 窗口:<NSThread: 0x600000295c40>{number = 3, name = (null)}
- 加鎖的情況
- (void)saleTicketSafe{
while (1) {
// 相當(dāng)于加鎖
// 原始信號(hào)量值為1 dispatch_semaphore_wait后減一 此時(shí)值為0 繼續(xù)往下執(zhí)行
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) { //如果還有票,繼續(xù)售賣
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else { //如果已賣完,關(guān)閉售票窗口
NSLog(@"所有火車票均已售完");
// 相當(dāng)于解鎖
// 當(dāng)前信號(hào)量值為0 dispatch_semaphore_signal后值加1 此時(shí)值為1 繼續(xù)往下執(zhí)行
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相當(dāng)于解鎖
// 當(dāng)前信號(hào)量值為0 dispatch_semaphore_signal后值加1 此時(shí)值為1 繼續(xù)往下執(zhí)行
dispatch_semaphore_signal(semaphoreLock);
}
}
打印結(jié)果
剩余票數(shù):4 窗口:<NSThread: 0x600001afb440>{number = 4, name = (null)}
剩余票數(shù):3 窗口:<NSThread: 0x600001acc440>{number = 3, name = (null)}
剩余票數(shù):2 窗口:<NSThread: 0x600001afb440>{number = 4, name = (null)}
剩余票數(shù):1 窗口:<NSThread: 0x600001acc440>{number = 3, name = (null)}
剩余票數(shù):0 窗口:<NSThread: 0x600001afb440>{number = 4, name = (null)}
所有火車票均已售完
最后重申一遍 此篇文章是對(duì)著iOS 多線程:『GCD』詳盡總結(jié) 此文抄的,勿噴。給自己加深映像用。具體請(qǐng)看上面的鏈接。 特別感謝 行走少年郎 的文章
