GCD多線程整理學(xué)習(xí)
GCD簡介
iOS中多核編程的解決方法。
主要用于優(yōu)化應(yīng)用程序以及支持多核處理器以及其他對稱多處理系統(tǒng)。iOS4推出
優(yōu)點
<pre>
1.可用于多核并行運算
2.自動利用更多CPU內(nèi)核(雙核罚斗,四核等)
3.GCD會自動管理線程生命周期(線程的創(chuàng)建和銷毀副渴、調(diào)度任務(wù))
4.不需要編寫線程管理代碼
</pre>
GCD 任務(wù)和隊列
任務(wù)
就是執(zhí)行操作的意思,也就是線程中執(zhí)行的代碼
<p>
執(zhí)行代碼又分:同步執(zhí)行筷弦、異步執(zhí)行
</p>
-
同步執(zhí)行
同步添加任務(wù)到指定隊列中溅潜,在添加的任務(wù)執(zhí)行結(jié)束之前术唬,會一直等待,直到隊列里面的任務(wù)完成之后再繼續(xù)執(zhí)行
只能在當(dāng)前線程中執(zhí)行任務(wù)滚澜,不具備開啟新線程的能力
-
異步執(zhí)行
異步添加任務(wù)到指定隊列中粗仓,它不會做任何等待,可以繼續(xù)執(zhí)行任務(wù)
可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
注意:異步執(zhí)行(asyn)雖然具有開啟線程的能力借浊,但是并不一定開啟新線程塘淑。這跟任務(wù)所指定的隊列類型有關(guān)。
隊列(Dispatch Queue)
執(zhí)行任務(wù)的等待隊列蚂斤,屬于特殊的線性列存捺,F(xiàn)IFO原則如下圖
隊列又分串行隊列和并發(fā)隊列,兩者符合FIFO曙蒸,區(qū)別在于:執(zhí)行順序不同捌治,開啟線程數(shù)不同
-
串行隊列(Serial Dispatch Queue)
任務(wù)逐一執(zhí)行,一個完成才到下一個
只開啟一個新線程
-
并發(fā)隊列(Concurrent Dispatch Queue)
可以讓多任務(wù)并發(fā)(同時)執(zhí)行
可以開啟多個線程纽窟,并同時執(zhí)行任務(wù)
注意:并發(fā)隊列的并發(fā)功能只有異步(dispatch_asyn)函數(shù)下有效
區(qū)別如圖
GCD使用
使用步驟:
創(chuàng)建隊列(串行肖油、并發(fā))
將任務(wù)追加到隊列中,系統(tǒng)會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)
隊列創(chuàng)建/獲取
- 使用
dispatch_queue_create
創(chuàng)建
//串行隊列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_CONCURRENT);
-
對于串行隊列师倔,特殊的串行隊列,主隊列 Main Dispatch Queue
所有主隊列任務(wù)都會在主線程執(zhí)行
使用
dispatch_get_main_queue()
獲取主隊列
-
對于并發(fā)隊列周蹭,GCD的默認(rèn)全局并發(fā)隊列 Global Dispatch Queue
- 使用
dispatch_get_global_queue
獲取,第一個參數(shù)趋艘,優(yōu)先級
- 使用
// 全局并發(fā)隊列的獲取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
####任務(wù)的創(chuàng)建方法
1. 同步執(zhí)行任務(wù):`dispatch_sync`
2. 異步執(zhí)行任務(wù):`dispatch_async`
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
// 這里放同步執(zhí)行任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
// 這里放異步執(zhí)行任務(wù)代碼
});
根據(jù)隊列可分為四種情況
> 1. 同步執(zhí)行+并發(fā)隊列
> 2. 異步執(zhí)行+并發(fā)隊列
> 3. 同步執(zhí)行+串行隊列
> 4. 異步執(zhí)行+串行隊列
對于之前的兩個特殊隊列,全局并發(fā)隊列凶朗、主隊列瓷胧。全局并發(fā)隊列可以作為普通并發(fā)隊列使用,但是主隊列有點特殊棚愤,故又多了兩種組合
> 5. 同步執(zhí)行+主隊列
> 6. 異步執(zhí)行+主隊列
總結(jié):
區(qū)別 | 并發(fā)隊列 | 串行隊列 | 主隊列
:--| :-- | :-- | :--
同步(sync)|不開新線程搓萧,串行|不開新線程,串行|主線程調(diào)用:死鎖卡死宛畦;<p>其他線程:不開新線程瘸洛,串行
異步(async)|有開新線程,并發(fā)|有開新線程(1條)次和,串行|不開新線程反肋,串行
###GCD的基本使用
####1.同步執(zhí)行 + 并發(fā)隊列
* 在當(dāng)前線程中執(zhí)行任務(wù),不會開啟新線程踏施,執(zhí)行完一個任務(wù)石蔗,再執(zhí)行下一個任務(wù),F(xiàn)IFO原則
/**
同步執(zhí)行 + 并發(fā)隊列
特點:在當(dāng)前線程中執(zhí)行任務(wù)畅形,不會開啟新線程养距,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)日熬。
*/
-
(void)syncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncConcurrent---end");
}
輸出結(jié)果:
> <pre>
currentThread—-{number = 1, name = main}
syncConcurrent—-begin
1—-{number = 1, name = main}
1—-{number = 1, name = main}
2—-{number = 1, name = main}
2—-{number = 1, name = main}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
syncConcurrent—-end
</pre>
結(jié)論:
1. 所有任務(wù)都在當(dāng)前線程執(zhí)行棍厌,沒有開啟新線程(`同步執(zhí)行`不具備開啟新線程的能力)
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)
3. 任務(wù)按順序執(zhí)行的。原因:雖然`并發(fā)隊列`可以開啟多個線程,并且同時執(zhí)行多個任務(wù)定铜,但是因為本身不能創(chuàng)建新線程阳液,只有當(dāng)前線程這一個線程(`同步任務(wù)`不具備開啟新線程的能力),所以也就不存在并發(fā)揣炕。而且當(dāng)前線程只有等待當(dāng)前隊列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后才能繼續(xù)接著執(zhí)行下面的操作(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)帘皿。所以任務(wù)只能一個接著一個按順序執(zhí)行,不能同時被執(zhí)行
>綜上所述畸陡,`同步執(zhí)行`無開新線程能力鹰溜,導(dǎo)致就算是并發(fā)隊列也不能只能在當(dāng)前線程執(zhí)行,從而導(dǎo)致只能串行執(zhí)行任務(wù)丁恭,不具備并發(fā)能力
####2.異步執(zhí)行 + 并發(fā)隊列
* 可以開啟多個線程曹动,任務(wù)交替(同時執(zhí)行)
/**
異步執(zhí)行 + 并發(fā)隊列
特點:可以開啟多個線程,任務(wù)交替(同時)執(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:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncConcurrent---end");
}
輸出結(jié)果
><pre>
currentThread—-{number = 1, name = main}
asyncConcurrent—-begin
asyncConcurrent—-end
2—-{number = 5, name = (null)}
3—-{number = 4, name = (null)}
1—-{number = 3, name = (null)}
3—-{number = 4, name = (null)}
1—-{number = 3, name = (null)}
2—-{number = 5, name = (null)}
</pre>
結(jié)論:
1. 除了當(dāng)前線程墓陈,系統(tǒng)又開了3個線程,并且任務(wù)交替/同時執(zhí)行第献。(`異步執(zhí)行`具備開啟新線程的能力贡必,且`并發(fā)隊列`可開啟多個線程,同時執(zhí)行多個任務(wù))
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后才執(zhí)行的庸毫。說明當(dāng)前線程沒有等待仔拟,而是直接開啟了新線程,在新線程中執(zhí)行任務(wù)(`異步執(zhí)行`不做等待飒赃,可以繼續(xù)執(zhí)行任務(wù))
>綜上所述利花,`異步執(zhí)行`有開啟新線程能力,所以并發(fā)隊列才能開啟多個線程载佳,`異步執(zhí)行`并不會等待炒事,而是繼續(xù)執(zhí)行當(dāng)前任務(wù)
####3.同步執(zhí)行 + 串行隊列
* 不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)蔫慧。任務(wù)是串行的羡洛,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)藕漱。
/**
同步執(zhí)行 + 串行隊列
特點:不會開啟新線程欲侮,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的肋联,執(zhí)行完一個任務(wù)威蕉,再執(zhí)行下一個任務(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:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncSerial---end");
}
輸出結(jié)果
<pre>
currentThread—-{number = 1, name = main}
syncSerial—-begin
1—-{number = 1, name = main}
1—-{number = 1, name = main}
2—-{number = 1, name = main}
2—-{number = 1, name = main}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
syncSerial—-end
</pre>
結(jié)論:
1. 所有任務(wù)都是在當(dāng)前線程執(zhí)行橄仍,并沒有開啟新的線程(`同步執(zhí)行`不具備開啟新線程的能力)
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行的(`同步線程`需要等待隊列的任務(wù)執(zhí)行結(jié)束)韧涨。
3. 任務(wù)是按順序執(zhí)行的(`串行隊列`每次只有一個任務(wù)被執(zhí)行牍戚,任務(wù)一個接一個按順序執(zhí)行)
>綜上所述,`同步執(zhí)行`沒有開啟新線程能力虑粥,`同步執(zhí)行`需要等待隊列任務(wù)執(zhí)行完才能繼續(xù)執(zhí)行
####4.異步執(zhí)行 + 串行隊列
* 會開啟新線程如孝,但是因為任務(wù)是串行的,執(zhí)行完一個任務(wù)娩贷,再執(zhí)行下一個任務(wù)
/**
異步執(zhí)行 + 串行隊列
特點:會開啟新線程第晰,但是因為任務(wù)是串行的,執(zhí)行完一個任務(wù)彬祖,再執(zhí)行下一個任務(wù)茁瘦。
*/
-
(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:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncSerial---end");
}
輸出結(jié)果
<pre>
currentThread—-{number = 1, name = main}
asyncSerial—-begin
asyncSerial—-end
1—-{number = 3, name = (null)}
1—-{number = 3, name = (null)}
2—-{number = 3, name = (null)}
2—-{number = 3, name = (null)}
3—-{number = 3, name = (null)}
3—-{number = 3, name = (null)}
</pre>
結(jié)論:
1. 開啟了一條新線程(`異步線程`具備開啟新線程的能力,`串行隊列`只開啟一個線程)
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后才執(zhí)行的(`異步執(zhí)行`不會做任何等待储笑,可以繼續(xù)執(zhí)行任務(wù))
3. 任務(wù)是按順序執(zhí)行的(`串行隊列`每次只有一個任務(wù)被執(zhí)行甜熔,任務(wù)一個接一個按順序執(zhí)行)
>綜上所述,`異步執(zhí)行`具備開啟新線程能力突倍,但不會等待腔稀,`串行隊列`只開啟一條新線程執(zhí)行任務(wù)
####5.同步執(zhí)行 + 主隊列
**主隊列:**
* GCD自帶的一種特殊的串行隊列
* 所有放在主隊列中的任務(wù),都會放在主線程中執(zhí)行
* 使用 `dispatch_get_main_queue()`獲得主隊列
####5.1在主線程中調(diào)用 <font size = 3 /font>`同步執(zhí)行 + 主隊列`</font>
* 互相等待卡死
/**
同步執(zhí)行 + 主隊列
特點(主線程調(diào)用):互等卡主不執(zhí)行羽历。
特點(其他線程調(diào)用):不會開啟新線程焊虏,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)窄陡。
*/
- (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:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"syncMain---end");
}
輸出結(jié)果
<pre>
currentThread—-{number = 1, name = main}
syncMain—-begin
(lldb)
</pre>
結(jié)論:
1. 在主線程中使用`同步執(zhí)行 + 主隊列`炕淮,追加到主線程的任務(wù)1拆火,2跳夭,3都不再執(zhí)行了,而且`syncMain---end`也沒有打印们镜,還報崩潰
**原因:**這是因為我們在主線程中執(zhí)行`syncMain`方法币叹,相當(dāng)于把`syncMain`任務(wù)放到了主線程的隊列中。而`同步線程`會等待當(dāng)前隊列中的任務(wù)執(zhí)行完畢模狭,才會接著執(zhí)行颈抚。那么當(dāng)我們把`任務(wù)1`追加到主隊列中,`任務(wù)1`就在等待主線程處理完`sysncMain`任務(wù)嚼鹉,而`syncMain`任務(wù)需要等待`任務(wù)1`執(zhí)行完畢才能接著執(zhí)行贩汉。那么,現(xiàn)在的情況就是`syncMain`任務(wù)和`任務(wù)1`都在等對方執(zhí)行完畢锚赤,相互等待匹舞,所以就卡死了
####5.2 在其他線程中調(diào)用 <font size = 3 /font>`同步執(zhí)行 + 主隊列`<\font>
* 不會開啟新線程,執(zhí)行完一個任務(wù)线脚,再執(zhí)行下一個任務(wù)
// 使用 NSThread 的 detachNewThreadSelector 方法會創(chuàng)建線程赐稽,并自動啟動線程執(zhí)行
selector 任務(wù)
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
輸出結(jié)果
<pre>
currentThread—-{number = 3, name = (null)}
syncMain—-begin
1—-{number = 1, name = main}
1—-{number = 1, name = main}
2—-{number = 1, name = main}
2—-{number = 1, name = main}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
syncMain—-end
</pre>
結(jié)論:
1. 所有任務(wù)都是在主線程(非當(dāng)前線程)中執(zhí)行叫榕,沒有開啟新線程(所有在`主隊列`的任務(wù)都會在`主線程`執(zhí)行)
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)
3. 任務(wù)是按順序執(zhí)行的(`主隊列`是`串行隊列`,每次只有一個任務(wù)執(zhí)行姊舵,任務(wù)一個接一個執(zhí)行)
為何不會卡孜铩?
因為`sysncMain`任務(wù)放在其他線程里括丁,而且任務(wù)1荞下,2,3都在追加到主隊列中躏将,這三個任務(wù)會在主線程中執(zhí)行锄弱。`syncMain`任務(wù)在其他線程中執(zhí)行到追加 任務(wù)1 到主隊列中,因為主隊列現(xiàn)在沒有正在執(zhí)行的任務(wù)祸憋,所以會直接執(zhí)行主隊列的 任務(wù)1会宪,之后 2,3 不會卡死
###6 異步執(zhí)行 + 主隊列
* 只在主線程中執(zhí)行任務(wù)蚯窥,執(zhí)行完一個再執(zhí)行下一個
/**
異步執(zhí)行 + 主隊列
特點:只在主線程中執(zhí)行任務(wù)掸鹅,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(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]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"asyncMain---end");
}
輸出結(jié)果
<pre>
currentThread—-{number = 1, name = main}
asyncMain—-begin
asyncMain—-end
1—-{number = 1, name = main}
1—-{number = 1, name = main}
2—-{number = 1, name = main}
2—-{number = 1, name = main}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
</pre>
結(jié)論
1. 所有任務(wù)都在當(dāng)前線程(主線程)執(zhí)行拦赠,并沒有開啟新的線程(雖然`異步執(zhí)行`具備開啟線程的能力巍沙,但因為是主隊列,所以所有任務(wù)都是在主線程中)
2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后執(zhí)行(異步執(zhí)行不會做任何等待荷鼠,可以繼續(xù)執(zhí)行任務(wù))
3. 任務(wù)是按順序執(zhí)行的(因為主隊列是`串行隊列`句携,每次只能執(zhí)行一個任務(wù))
###線程通訊
通過 `dispatch_async`、`dispatch_sync`進行通訊
###GCD的其他方法
####1.GCD 柵欄方法:dispatch_barrier_async
* 有時我們需要異步執(zhí)行兩組操作允乐,而且第一組操作執(zhí)行完之后矮嫉,才能開始執(zhí)行第二組操作牍疏。這樣我們就需要一個相當(dāng)于 `柵欄` 一樣的一個方法將兩組異步執(zhí)行的操作組給分割起來蠢笋,當(dāng)然的操作組里面可以包含一個或多個任務(wù),這就需要用到`dispatch_barrier_async`方法在兩個操作組間形成柵欄鳞陨。
<p>`dispatch_barrier_async`函數(shù)會等待前邊追加到并發(fā)隊列中的任務(wù)全部執(zhí)行完畢之后昨寞,再將指定的任務(wù)追加到該異步隊列中。然后在`dispatch_barrier_async`函數(shù)追加的任務(wù)執(zhí)行完畢之后厦滤,異步隊列才恢復(fù)為一般動作服赎,接著追加任務(wù)到該異步隊列并開始執(zhí)行讼撒。如下圖![圖](http://upload-images.jianshu.io/upload_images/10443211-27884f07b52dfa89.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
代碼如下
/**
- 柵欄方法 dispatch_barrier_async
*/
-
(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]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_barrier_async(queue, ^{
// 追加任務(wù) barrier
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)4
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"4---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
}
輸出結(jié)果:
<pre>
1—-{number = 4, name = (null)}
2—-{number = 3, name = (null)}
1—-{number = 4, name = (null)}
2—-{number = 3, name = (null)}
barrier—-{number = 4, name = (null)}
barrier—-{number = 4, name = (null)}
4—-{number = 3, name = (null)}
3—-{number = 4, name = (null)}
4—-{number = 3, name = (null)}
3—-{number = 4, name = (null)}
</pre>
結(jié)論:
1. 在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作,最后再執(zhí)行后邊的操作
####2.GCD 延時方法:dispatch_after
`dispatch_after`:在指定時間之后執(zhí)行某個任務(wù)命爬。
需要注意的是:`dispatch_after`函數(shù)并不是在指定時間之后才開始執(zhí)行處理笔宿,而是在指定時間之后**<a>將任務(wù)追加到主隊列</a>**中律姨。嚴(yán)格來說,這個時間并不絕對準(zhǔn)確限寞。
####3.GCD 一次性代碼(只執(zhí)行一次):dispatch_once
* 我們在創(chuàng)建單例、或者有整個程度運行過程中只執(zhí)行一次的代碼時使用
* `dispatch_once`函數(shù)能保證代碼只執(zhí)行一次仰坦,并且即使在多線程的環(huán)境下也能保證線程安全
/**
- 一次性代碼(只執(zhí)行一次)dispatch_once
*/
-
(void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
}
####4.GCD 快速迭代方法:dispatch_apply
* 通常我們會用 for循環(huán) 遍歷履植,但是GCD給我們提供了快速迭代的函數(shù)`dispatch_apply`。`dipatch_apply`按照指定的次數(shù)將指定的任務(wù)追加到指定的隊列中悄晃,并等待全部隊列執(zhí)行結(jié)束
如果是在串行隊列中使用`dipatch_apply`玫霎,那么就跟 for 循環(huán)一樣,按順序同步執(zhí)行妈橄∈可這樣就體現(xiàn)不出快速迭代的意義了。
<p>我們可以利用并發(fā)隊列進行異步執(zhí)行眷蚓。比如說遍歷0-5這6個數(shù)字鼻种,for循環(huán)的做法是每次取出一個元素,逐個遍歷沙热。`dispatch_apply`可以在多個線程中同時(異步)遍歷多個數(shù)字叉钥。
<p>還有一點,無論是在串行隊列篙贸,還是異步隊列中投队,dipatch_apply 都會等待全部任務(wù)執(zhí)行完畢,這點就像是同步操作爵川,也像是隊列組中的`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");
}
輸出結(jié)果
<pre>
apply—-begin
1—-{number = 3, name = (null)}
0—-{number = 1, name = main}
2—-{number = 4, name = (null)}
3—-{number = 5, name = (null)}
4—-{number = 3, name = (null)}
5—-{number = 1, name = main}
apply—-end
</pre>
結(jié)論
因為是在并發(fā)隊列中異步執(zhí)行任務(wù)敷鸦,所以各個任務(wù)的執(zhí)行時間 長短不定,最后順序也不定寝贡,但是`apply_end`一定在最后執(zhí)行扒披。這是因為`dispatch_apply`函數(shù)會等待全部任務(wù)執(zhí)行完畢。
>綜上所述兔甘,`dispatch_apply`適用于谎碍,當(dāng)前需要等待某堆任務(wù)(可以是并發(fā)任務(wù)隊列)完成后才能繼續(xù)操作的情況
####5.GCD 的隊列組:dispatch_group
有時候我們會有這樣的需求:分別異步執(zhí)行2個耗時任務(wù)鳞滨,然后當(dāng)2個任務(wù)都執(zhí)行完再回到主線程洞焙,這時就要用到 GCD 的隊列組。
* 要調(diào)用隊列組的`dispatch_group_async`先把任務(wù)放到隊列中拯啦,然后將隊列放入隊列組中澡匪。或者使用隊列組的 `dispatch_group_enter`褒链、`dispatch_group_leave`組合 來實現(xiàn)`dispatch_group_async`.
* 調(diào)用隊列組的`dispatch_group_notify`回到指定線程執(zhí)行任務(wù)唁情。或者使用`dispatch_group_wait`回到當(dāng)前線程繼續(xù)向下執(zhí)行(會阻塞當(dāng)前線程)
#####5.1 dipatch\_group\_notify
* 監(jiān)聽 group 中任務(wù)的完成狀態(tài)甫匹,當(dāng)所有的任務(wù)都執(zhí)行完成后甸鸟,追加任務(wù)到group中惦费,并執(zhí)行任務(wù)
/**
- 隊列組 dispatch_group_notify
*/
-
(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]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
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]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
}
輸出結(jié)果:
<pre>
currentThread—-{number = 1, name = main}
group—-begin
1—-{number = 4, name = (null)}
2—-{number = 3, name = (null)}
2—-{number = 3, name = (null)}
1—-{number = 4, name = (null)}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
group—-end
</pre>
結(jié)論:
當(dāng)所有任務(wù)都執(zhí)行完成之后才執(zhí)行`dispatch_group_notify`block中的任務(wù)
####5.2 dispatch\_group\_wait
* 暫停當(dāng)前線程(阻塞當(dāng)前線程)薪贫,等待指定的group 中的任務(wù)執(zhí)行完成后,才會繼續(xù)執(zhí)行
/**
- 隊列組 dispatch_group_wait
*/
-
(void)groupWait {
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]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
// 等待上面的任務(wù)全部完成后刻恭,會往下繼續(xù)執(zhí)行(會阻塞當(dāng)前線程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
輸出結(jié)果:
<pre>
currentThread—-{number = 1, name = main}
group—-begin
2—-{number = 4, name = (null)}
1—-{number = 3, name = (null)}
2—-{number = 4, name = (null)}
1—-{number = 3, name = (null)}
group—-end
</pre>
結(jié)論:
dispatch\_group\_wait會等待所有g(shù)roup任務(wù)完成后才繼續(xù)執(zhí)行
####5.3 dispatch\_group\_enter瞧省、dispatch\_group\_leave
* `dispatch_group_enter`標(biāo)志著一個任務(wù)追加到group,執(zhí)行一次鳍贾,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)+1
* `dispatch_group_leave`標(biāo)志著一個任務(wù)離開group鞍匾,執(zhí)行一次,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)-1
* 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0時骑科,才會拿`dispatch_group_wait`解除阻塞橡淑,以及執(zhí)行追加到`dispatch_group_notify`中的任務(wù)
/**
- 隊列組 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]; // 模擬耗時操作
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]; // 模擬耗時操作
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]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
// // 等待上面的任務(wù)全部完成后梳码,會往下繼續(xù)執(zhí)行(會阻塞當(dāng)前線程)
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//
// NSLog(@"group---end");
}
輸出結(jié)果
<pre>
currentThread—-{number = 1, name = main}
group—-begin
1—-{number = 4, name = (null)}
2—-{number = 3, name = (null)}
1—-{number = 4, name = (null)}
2—-{number = 3, name = (null)}
3—-{number = 1, name = main}
3—-{number = 1, name = main}
group—-end
</pre>
結(jié)論
當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行 `dispatch_group_notify`中的任務(wù)伍掀,這里 `dispatch_group_enter`掰茶、`dispatch_group_leave`組合,其實等同于`dispatch_group_async`.
####6.GCD 信號量:dispatch_semaphore
GCD 中的信號量是指 Dispatch Semaphore,是持有計數(shù)的信號蜜笤。類似于高速路收費站的欄桿濒蒋。可以通過時把兔,打開欄桿沪伙,不可以通過時,關(guān)閉欄桿县好。在Dispatch_Semaphore 中围橡,使用計數(shù)來完成這個功能,計數(shù)為0時等待缕贡,不可通過翁授。計數(shù)為1或者大于1時,計數(shù)減1且不等待晾咪,可通過收擦。
Dispatch Semaphore 提供了三個函數(shù)
* `dispatch_semaphore_create`:創(chuàng)建一個Semaphore并初始化信號的總量
* `dispatch_semaphore_signal`:發(fā)送一個信號,讓信號問題加1
* `dispatch_semaphore_wait`:可以使用總信號量減1谍倦,當(dāng)信號總量為0時就會一直等待(阻塞所有線程)塞赂,否則就可以正常執(zhí)行
>注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續(xù)執(zhí)行昼蛀,然后使用信號量
Dispatch Semaphore 在實際開發(fā)中主要用于:
1. 保持線程同步宴猾,將異步執(zhí)行任務(wù)轉(zhuǎn)換成同步執(zhí)行任務(wù)
2. 保證線程安全圆存,為線程加鎖
####6.1 Dispatch Semaphore 線程同步
當(dāng)異步執(zhí)行耗時任務(wù),并使用異步執(zhí)行的結(jié)果進行一些額外的操作時仇哆,我們要用到這個辽剧。換句話說,相當(dāng)于税产,將異步執(zhí)行任務(wù)轉(zhuǎn)換成同步執(zhí)行任務(wù).比如:AFNetworking中AFURLSessionManager.m 里面的 `tasksForKeyPath:`方法怕轿。通過引入信號量的方式,等待異步執(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;
}
下面衫冻,我們來利用 Dispatch Semaphore 實現(xiàn)線程同步诀紊,將異步任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
/**
- semaphore 線程同步
*/
- (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, ^{
// 追加任務(wù)1
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %zd",number);
}
輸出結(jié)果:
<pre>
currentThread—-{number = 1, name = main}
semaphore—-begin
1—-{number = 3, name = (null)}
semaphore—-end,number = 100
</pre>
結(jié)論:
* `semaphore---end`是在執(zhí)行完 `number = 100;`之后才打印的。而且輸出結(jié)果 number 為100隅俘;
這是因為`異步執(zhí)行`不會做任務(wù)等待邻奠,可以繼續(xù)執(zhí)行任務(wù),`異步執(zhí)行`將任務(wù)1追加到隊列之后为居,不做等待碌宴,接著執(zhí)行 `dispatch_semaphore_wait`方法。此時 semaphore == 0蒙畴,當(dāng)前線程進入等待狀態(tài)贰镣。然后異步任務(wù)1開始執(zhí)行。任務(wù)1執(zhí)行到`dispatch_semaphore_signal`之后膳凝,總信號量碑隆,此時為1,`dispatch_semaphore_wait`方法使用總信號量減1蹬音,正在被阻塞的線程恢復(fù)繼續(xù)執(zhí)行上煤。
####6.2 Dispatch Semaphore 線程安全和線程同步(為線程加鎖)
最后的學(xué)習(xí)總結(jié):
- GCD會自動管理線程生命周期
- GCD會充分使用CPU內(nèi)核
- GCD中同步線程不能創(chuàng)建新線程,只能使用當(dāng)前線程著淆,故無論是串行還是并發(fā)都是串行執(zhí)行任務(wù)
- GCD中異步線程可以創(chuàng)建新線程劫狠,并發(fā)隊列會根據(jù)實際情況創(chuàng)建1條或者多條線程并發(fā)執(zhí)行執(zhí)行,串行隊列只開辟一條新線程并且新線程中串行執(zhí)行任務(wù)
- 主線程為特殊同步線程牧抽,主隊列是串行隊列且只能在主線程中串行執(zhí)行嘉熊,不可創(chuàng)建新的主線程遥赚,故在主線程中通過同步添加任務(wù)到主隊列中會形成相互等的死鎖狀態(tài)
-
dispatch_barrier_async
欄柵扬舒,通過dispatch_barrier_async
或隔開隊列執(zhí)行 無論dispatch_barrier_async
前是串行還是并發(fā)隊列,在隊列任務(wù)全部執(zhí)行完之前 會一直等待凫佛,完成后執(zhí)行dispatch_barrier_async
中的代碼塊讲坎,執(zhí)行完后才會繼續(xù)往下執(zhí)行 -
dispatch_group_async
將任務(wù)添加到隊列后再把隊列添加到group里 當(dāng)group執(zhí)行完會執(zhí)行dispatch_group_notify
里面的代碼塊 -
dispatch_group_async
也可以在添加完任務(wù)后dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
阻塞線程來達到執(zhí)行完group后才執(zhí)行其他代碼的目的 -
dispatch_group_async
也可以用dispatch_group_enter
(追加group+1)孕惜、dispatch_group_leave
(group-1),但一定要配套使用 -
dispatch_semaphore
dispatch_semaphore_create
(創(chuàng)建)、dispatch_semaphore_signal
(發(fā)送信號晨炕、發(fā)送后dispatch_semaphore_wait
會進行判斷是否能解除線程阻塞)衫画、dispatch_semaphore_wait
(判斷信號是否為0,為0時線程會一直等待阻塞線程直接信號量變?yōu)?之后)