簡(jiǎn)介
GCD:(Grand Central Dispatch)GCD是基于C語(yǔ)言的。
GCD本身是蘋果公司為多核的并行運(yùn)算提出的解決方案,它完全由系統(tǒng)管理線程,我們不需要編寫線程代碼,只需要在適當(dāng)?shù)恼{(diào)度隊(duì)列中(dispatch queue)實(shí)現(xiàn)要執(zhí)行的任務(wù)就好。GCD會(huì)負(fù)責(zé)創(chuàng)建線程和調(diào)度你的任務(wù),系統(tǒng)直接提供線程管理。
GCD能推遲昂貴的計(jì)算任務(wù),并在后臺(tái)運(yùn)行,從而可以改善工程的性能。
GCD能提供易于我們使用的并發(fā)模型從而幫我們避開(kāi)并發(fā)陷阱。
調(diào)度隊(duì)列(dispath queue)
queue 是GCD的一個(gè)重要概念,我們可以這樣理解:我們要執(zhí)行一個(gè)長(zhǎng)期任務(wù),可以將這個(gè)任務(wù)分成多個(gè)工作單元,并將這些工作單元添加到
dispath queue中,系統(tǒng)則會(huì)為我們管理這些工作單元,,為我們?cè)诙鄠€(gè)線程上執(zhí)行工作單元,我們不需要直接啟動(dòng)和管理后臺(tái)線程。
GCD的dispach queue 是嚴(yán)格按照先進(jìn)先出的的原則工作的,添加到dispath queue的工作單元將始終按照加入dispath queue的順序啟動(dòng)。
一、并行隊(duì)列(concurrent dispath queue)
- 并發(fā)是可以同時(shí)執(zhí)行多個(gè)任務(wù),實(shí)際上也是按照先進(jìn)先出(FIFO)的原則執(zhí)行的,并發(fā)會(huì)在上一個(gè)任務(wù)即將執(zhí)行完之前就出列下一個(gè)任務(wù)并執(zhí)行。
- 并發(fā)的任務(wù)數(shù)量,會(huì)根據(jù)應(yīng)用和系統(tǒng)動(dòng)態(tài)變化,例如:可用的核數(shù)量、其他進(jìn)程正在執(zhí)行的工作量、其他串行的工作量等等。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
DISPATCH_QUEUE_PRIORITY_HIGH 2 優(yōu)先級(jí)最高
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 優(yōu)先級(jí)默認(rèn)
DISPATCH_QUEUE_PRIORITY_LOW (-2) 優(yōu)先級(jí)最低
二、串行隊(duì)列(serial dispatch queue)
- 應(yīng)用任務(wù)按順序執(zhí)行,串行queue每次只執(zhí)行一個(gè)任務(wù),可替代鎖,和鎖不一樣的是,串行queue確保任務(wù)按可預(yù)測(cè)的順序執(zhí)行。并且不容易產(chǎn)生死鎖。
dispatch_queue_t queue = dispatch_queue_create("cn.test.queue", NULL);
第一個(gè)參數(shù)是queue的名字
第二個(gè)參數(shù)是一組queue的屬性
三、同步&異步
- 同步:函數(shù)執(zhí)行玩這個(gè)任務(wù)才能執(zhí)行下一個(gè)任務(wù)。對(duì)于GCD而言:同步函數(shù)執(zhí)行一個(gè)任務(wù),直到執(zhí)行完成它的預(yù)定任務(wù)后才返回。
- 異步:函數(shù)不會(huì)等到任務(wù)完成,它會(huì)立即執(zhí)行下一個(gè)任務(wù),同時(shí)當(dāng)前任務(wù)會(huì)繼續(xù)在后臺(tái)執(zhí)行,對(duì)于GCD而言:與同步相反,它會(huì)立即返回,而任務(wù)繼續(xù)執(zhí)行,它并不會(huì)等待任務(wù)完成再返回。
NSLog(@"當(dāng)前線程:%@", [NSThread currentThread]);
//創(chuàng)建一個(gè)并發(fā)線程
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//創(chuàng)建一個(gè)異步任務(wù)
dispatch_async(queue, ^{
NSLog(@"異步任務(wù),當(dāng)前線程:%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"同步任務(wù),當(dāng)前線程:%@",[NSThread currentThread]);
});
打印消息:
當(dāng)前線程:<NSThread: 0x7fb102508120>{number = 1, name = main}
異步任務(wù),當(dāng)前線程:<NSThread: 0x7fb102782970>{number = 2, name = (null)}
同步任務(wù),當(dāng)前線程:<NSThread: 0x7fb102508120>{number = 1, name = main}
四、循環(huán)迭代(dispatch_apply)
- 如果每次迭代與其他迭代獨(dú)立無(wú)關(guān),,而且循環(huán)迭代執(zhí)行順序也無(wú)關(guān)緊要的話,我們可以嘗試使用
dispath_apply或dispatch_apply_f代替for循環(huán)。
這兩個(gè)函數(shù)可以同時(shí)并發(fā)多個(gè)循環(huán)迭代,這樣可以大大提高效率。 - 用
dispatch_apply或dispatch_apply_f時(shí)你可以指定串行或并發(fā) queue。并發(fā)queue允許同時(shí)執(zhí)行多個(gè)循環(huán)迭代,而串行queue就沒(méi)太大必要使用了。
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//你傳遞的block必須包含一個(gè)size_t類型的參數(shù),用來(lái)標(biāo)識(shí)當(dāng)前循環(huán)迭代。第一次迭代這個(gè)參數(shù)值為0,最后一次值為count - 1
size_t count = 8;
dispatch_apply(count, queue, ^(size_t t) {
NSLog(@"%zu",t);
});
打印結(jié)果:
2016-07-13 11:10:33.423 GCDTest[1867:83704] 0
2016-07-13 11:10:33.423 GCDTest[1867:83703] 2
2016-07-13 11:10:33.423 GCDTest[1867:83662] 1
2016-07-13 11:10:33.423 GCDTest[1867:83714] 3
2016-07-13 11:10:33.423 GCDTest[1867:83704] 4
2016-07-13 11:10:33.424 GCDTest[1867:83703] 5
2016-07-13 11:10:33.424 GCDTest[1867:83662] 6
2016-07-13 11:10:33.424 GCDTest[1867:83714] 7
由此可見(jiàn),這些迭代是并發(fā)執(zhí)行的,并且順序不固定,
五、暫停和繼續(xù)queue
- 我們可以使用dispatch_suspend函數(shù)暫停一個(gè)queue以阻止它執(zhí)行block對(duì)象;使用dispatch_resume函數(shù)繼續(xù)dispatch queue。
- 掛起一個(gè)queue不會(huì)導(dǎo)致正在執(zhí)行的block停止,只會(huì)導(dǎo)致不再繼續(xù)執(zhí)行還未執(zhí)行的任務(wù)。dispatch_resume會(huì)喚醒已掛起的dispatch queue。
- 調(diào)用dispatch_suspend會(huì)增加queue的引用計(jì)數(shù),調(diào)用dispatch_resume則減少queue的引用計(jì)數(shù)。
- 當(dāng)引用計(jì)數(shù)大于0時(shí),queue就保持掛起狀態(tài)。因此你必須對(duì)應(yīng)地調(diào)用suspend和resume函數(shù)(必須成對(duì)出現(xiàn))。
- 掛起和繼續(xù)是異步的,而且只在執(zhí)行block之間(比如在執(zhí)行一個(gè)新的block之前或之后)生效。
NSLog(@"當(dāng)前線程:%@", [NSThread currentThread]);
//創(chuàng)建一個(gè)串發(fā)線程
dispatch_queue_t queue;
queue = dispatch_queue_create("cn.test.queue", NULL);
dispatch_async(queue, ^{
NSLog(@"異步任務(wù),當(dāng)前線程:%@",[NSThread currentThread]);
});
//掛起queue
dispatch_suspend(queue);
dispatch_sync(queue, ^{
NSLog(@"同步任務(wù),當(dāng)前線程:%@",[NSThread currentThread]);
});
//結(jié)束掛起
dispatch_resume(queue);
打印結(jié)果
當(dāng)前線程:<NSThread: 0x7fdb53d00550>{number = 1, name = main}
六、常用
// 后臺(tái)執(zhí)行:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
});
// 主線程執(zhí)行:
dispatch_async(dispatch_get_main_queue(), ^{
// something
});
// 一次性執(zhí)行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
// 延遲2秒執(zhí)行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
// 自定義dispatch_queue_t
dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
dispatch_async(urls_queue, ^{
// your code
});
// 銷毀隊(duì)列
dispatch_release(urls_queue);
// 合并匯總結(jié)果
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行執(zhí)行的線程一
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行執(zhí)行的線程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
// 匯總結(jié)果
});
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
將需要執(zhí)行的任務(wù)對(duì)象指定到不同的隊(duì)列中去處理,比如說(shuō)有兩個(gè)隊(duì)列dispatchA和dispatchB,這時(shí)把dispatchA指派到dispatchB:
七、一些概念
- 串行&并行: 任務(wù)串行執(zhí)行就是每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)并發(fā)執(zhí)行就是在同一時(shí)間可以有多個(gè)任務(wù)被執(zhí)行。
- 臨界區(qū): 就是一段代碼不能被并發(fā)執(zhí)行,也就是,兩個(gè)線程不能同時(shí)執(zhí)行這段代碼。這很常見(jiàn),因?yàn)榇a去操作一個(gè)共享資源,例如一個(gè)變量若能被并發(fā)進(jìn)程訪問(wèn),那么它的值將不再可信。
- 競(jìng)態(tài)條件: 這種狀況是指基于特定序列或時(shí)機(jī)的事件的軟件系統(tǒng)以不受控制的方式運(yùn)行的行為,例如程序的并發(fā)任務(wù)執(zhí)行的確切順序。競(jìng)態(tài)條件可導(dǎo)致無(wú)法預(yù)測(cè)的行為,而不能通過(guò)代碼檢查立即發(fā)現(xiàn)。
- 死鎖:所謂的死鎖是指兩個(gè)線程它們都卡住了,并等待對(duì)方完成或執(zhí)行其它操作。第一個(gè)不能完成是因?yàn)樗诘却诙€(gè)的完成。但第二個(gè)也不能完成,因?yàn)樗诘却谝粋€(gè)的完成。
- 線程安全: 線程安全是代碼能在多線程或并發(fā)任務(wù)中被安全的調(diào)用,而不會(huì)導(dǎo)致任何問(wèn)題(數(shù)據(jù)損壞,崩潰,等)。線程不安全的代碼在某個(gè)時(shí)刻只能在一個(gè)環(huán)境中運(yùn)行。一個(gè)線程安全代碼的例子是 NSDictionary 。你可以在同一時(shí)間在多個(gè)線程中使用它而不會(huì)有問(wèn)題。另一方面,NSMutableDictionary 就不是線程安全的,應(yīng)該保證一次只能有一個(gè)線程訪問(wèn)它。