gcd

在這兩部分的系列中采驻,第一個(gè)部分的將解釋 GCD 是做什么的审胚,并從許多基本的 GCD 函數(shù)中找出幾個(gè)來展示。在第二部分挑宠,你將學(xué)到幾個(gè) GCD 提供的高級函數(shù)菲盾。

什么是 GCD

GCD 是 libdispatch 的市場名稱,而 libdispatch 作為 Apple 的一個(gè)庫各淀,為并發(fā)代碼在多核硬件(跑 iOS 或 OS X )上執(zhí)行提供有力支持懒鉴。它具有以下優(yōu)點(diǎn):

1.GCD 能通過推遲昂貴計(jì)算任務(wù)并在后臺運(yùn)行它們來改善你的應(yīng)用的響應(yīng)性能。

2.GCD 提供一個(gè)易于使用的并發(fā)模型而不僅僅只是鎖和線程碎浇,以幫助我們避開并發(fā)陷阱临谱。

3.GCD 具有在常見模式(例如單例)上用更高性能的原語優(yōu)化你的代碼的潛在能力。

本教程假設(shè)你對 Block 和 GCD 有基礎(chǔ)了解奴璃。如果你對 GCD 完全陌生悉默,先看看 iOS 上的多線程和 GCD 入門教程 學(xué)習(xí)其要領(lǐng)。

GCD 術(shù)語

要理解 GCD 苟穆,你要先熟悉與線程和并發(fā)相關(guān)的幾個(gè)概念抄课。這兩者都可能模糊和微妙,所以在開始 GCD 之前先簡要地回顧一下它們雳旅。

Serial vs. Concurrent 串行 vs. 并發(fā)

這些術(shù)語描述當(dāng)任務(wù)相對于其它任務(wù)被執(zhí)行跟磨,任務(wù)串行執(zhí)行就是每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)并發(fā)執(zhí)行就是在同一時(shí)間可以有多個(gè)任務(wù)被執(zhí)行攒盈。

雖然這些術(shù)語被廣泛使用抵拘,本教程中你可以將任務(wù)設(shè)定為一個(gè) Objective-C 的 Block 。不明白什么是 Block 型豁?看看 iOS 5 教程中的如何使用 Block 僵蛛。實(shí)際上,你也可以在 GCD 上使用函數(shù)指針迎变,但在大多數(shù)場景中充尉,這實(shí)際上更難于使用。Block 就是更加容易些氏豌!

Synchronous vs. Asynchronous 同步 vs. 異步

在 GCD 中喉酌,這些術(shù)語描述當(dāng)一個(gè)函數(shù)相對于另一個(gè)任務(wù)完成,此任務(wù)是該函數(shù)要求 GCD 執(zhí)行的泵喘。一個(gè)同步函數(shù)只在完成了它預(yù)定的任務(wù)后才返回。

一個(gè)異步函數(shù)般妙,剛好相反纪铺,會立即返回,預(yù)定的任務(wù)會完成但不會等它完成碟渺。因此鲜锚,一個(gè)異步函數(shù)不會阻塞當(dāng)前線程去執(zhí)行下一個(gè)函數(shù)。

注意——當(dāng)你讀到同步函數(shù)“阻塞(Block)”當(dāng)前線程,或函數(shù)是一個(gè)“阻塞”函數(shù)或阻塞操作時(shí)芜繁,不要被搞糊涂了旺隙!動(dòng)詞“阻塞”描述了函數(shù)如何影響它所在的線程而與名詞“代碼塊(Block)”沒有關(guān)系。代碼塊描述了用 Objective-C 編寫的一個(gè)匿名函數(shù)骏令,它能定義一個(gè)任務(wù)并被提交到 GCD 蔬捷。

譯者注:中文不會有這個(gè)問題,“阻塞”和“代碼塊”是兩個(gè)詞榔袋。

Critical Section 臨界區(qū)

就是一段代碼不能被并發(fā)執(zhí)行周拐,也就是,兩個(gè)線程不能同時(shí)執(zhí)行這段代碼凰兑。這很常見妥粟,因?yàn)榇a去操作一個(gè)共享資源,例如一個(gè)變量若能被并發(fā)進(jìn)程訪問吏够,那么它很可能會變質(zhì)(譯者注:它的值不再可信)勾给。

Race Condition 競態(tài)條件

這種狀況是指基于特定序列或時(shí)機(jī)的事件的軟件系統(tǒng)以不受控制的方式運(yùn)行的行為,例如程序的并發(fā)任務(wù)執(zhí)行的確切順序锅知。競態(tài)條件可導(dǎo)致無法預(yù)測的行為播急,而不能通過代碼檢查立即發(fā)現(xiàn)。

Deadlock 死鎖

兩個(gè)(有時(shí)更多)東西——在大多數(shù)情況下喉镰,是線程——所謂的死鎖是指它們都卡住了旅择,并等待對方完成或執(zhí)行其它操作。第一個(gè)不能完成是因?yàn)樗诘却诙€(gè)的完成侣姆。但第二個(gè)也不能完成生真,因?yàn)樗诘却谝粋€(gè)的完成。

Thread Safe 線程安全

線程安全的代碼能在多線程或并發(fā)任務(wù)中被安全的調(diào)用捺宗,而不會導(dǎo)致任何問題(數(shù)據(jù)損壞柱蟀,崩潰,等)蚜厉。線程不安全的代碼在某個(gè)時(shí)刻只能在一個(gè)上下文中運(yùn)行长已。一個(gè)線程安全代碼的例子是 NSDictionary 。你可以在同一時(shí)間在多個(gè)線程中使用它而不會有問題昼牛。另一方面术瓮,NSMutableDictionary 就不是線程安全的,應(yīng)該保證一次只能有一個(gè)線程訪問它贰健。

Context Switch 上下文切換

一個(gè)上下文切換指當(dāng)你在單個(gè)進(jìn)程里切換執(zhí)行不同的線程時(shí)存儲與恢復(fù)執(zhí)行狀態(tài)的過程胞四。這個(gè)過程在編寫多任務(wù)應(yīng)用時(shí)很普遍,但會帶來一些額外的開銷伶椿。

Concurrency vs Parallelism 并發(fā)與并行

并發(fā)和并行通常被一起提到辜伟,所以值得花些時(shí)間解釋它們之間的區(qū)別始藕。

并發(fā)代碼的不同部分可以“同步”執(zhí)行序宦。然而,該怎樣發(fā)生或是否發(fā)生都取決于系統(tǒng)。多核設(shè)備通過并行來同時(shí)執(zhí)行多個(gè)線程舌稀;然而决左,為了使單核設(shè)備也能實(shí)現(xiàn)這一點(diǎn)耘子,它們必須先運(yùn)行一個(gè)線程液南,執(zhí)行一個(gè)上下文切換,然后運(yùn)行另一個(gè)線程或進(jìn)程廊佩。這通常發(fā)生地足夠快以致給我們并發(fā)執(zhí)行地錯(cuò)覺囚聚,如下圖所示:

雖然你可以編寫代碼在 GCD 下并發(fā)執(zhí)行,但 GCD 會決定有多少并行的需求标锄。并行要求并發(fā)顽铸,但并發(fā)并不能保證并行。

更深入的觀點(diǎn)是并發(fā)實(shí)際上是關(guān)于構(gòu)造料皇。當(dāng)你在腦海中用 GCD 編寫代碼谓松,你組織你的代碼來暴露能同時(shí)運(yùn)行的多個(gè)工作片段,以及不能同時(shí)運(yùn)行的那些践剂。如果你想深入此主題鬼譬,看看 this excellent talk by Rob Pike 。

Queues 隊(duì)列

GCD 提供有 dispatch queues 來處理代碼塊逊脯,這些隊(duì)列管理你提供給 GCD 的任務(wù)并用 FIFO 順序執(zhí)行這些任務(wù)优质。這就保證了第一個(gè)被添加到隊(duì)列里的任務(wù)會是隊(duì)列中第一個(gè)開始的任務(wù),而第二個(gè)被添加的任務(wù)將第二個(gè)開始军洼,如此直到隊(duì)列的終點(diǎn)巩螃。

所有的調(diào)度隊(duì)列(dispatch queues)自身都是線程安全的,你能從多個(gè)線程并行的訪問它們匕争。 GCD 的優(yōu)點(diǎn)是顯而易見的避乏,即當(dāng)你了解了調(diào)度隊(duì)列如何為你自己代碼的不同部分提供線程安全。關(guān)于這一點(diǎn)的關(guān)鍵是選擇正確類型的調(diào)度隊(duì)列和正確的調(diào)度函數(shù)來提交你的工作甘桑。

在本節(jié)你會看到兩種調(diào)度隊(duì)列拍皮,都是由 GCD 提供的,然后看一些描述如何用調(diào)度函數(shù)添加工作到隊(duì)列的列子跑杭。

Serial Queues 串行隊(duì)列

這些任務(wù)的執(zhí)行時(shí)機(jī)受到 GCD 的控制铆帽;唯一能確保的事情是 GCD 一次只執(zhí)行一個(gè)任務(wù),并且按照我們添加到隊(duì)列的順序來執(zhí)行德谅。

由于在串行隊(duì)列中不會有兩個(gè)任務(wù)并發(fā)運(yùn)行锄贼,因此不會出現(xiàn)同時(shí)訪問臨界區(qū)的風(fēng)險(xiǎn);相對于這些任務(wù)來說女阀,這就從競態(tài)條件下保護(hù)了臨界區(qū)宅荤。所以如果訪問臨界區(qū)的唯一方式是通過提交到調(diào)度隊(duì)列的任務(wù),那么你就不需要擔(dān)心臨界區(qū)的安全問題了浸策。

Concurrent Queues 并發(fā)隊(duì)列

在并發(fā)隊(duì)列中的任務(wù)能得到的保證是它們會按照被添加的順序開始執(zhí)行冯键,但這就是全部的保證了。任務(wù)可能以任意順序完成庸汗,你不會知道何時(shí)開始運(yùn)行下一個(gè)任務(wù)惫确,或者任意時(shí)刻有多少 Block 在運(yùn)行。再說一遍蚯舱,這完全取決于 GCD 改化。

下圖展示了一個(gè)示例任務(wù)執(zhí)行計(jì)劃,GCD 管理著四個(gè)并發(fā)任務(wù):

注意 Block 1,2 和 3 都立馬開始運(yùn)行枉昏,一個(gè)接一個(gè)陈肛。在 Block 0 開始后,Block 1等待了好一會兒才開始兄裂。同樣句旱, Block 3 在 Block 2 之后才開始,但它先于 Block 2 完成晰奖。

何時(shí)開始一個(gè) Block 完全取決于 GCD 谈撒。如果一個(gè) Block 的執(zhí)行時(shí)間與另一個(gè)重疊,也是由 GCD 來決定是否將其運(yùn)行在另一個(gè)不同的核心上匾南,如果那個(gè)核心可用啃匿,否則就用上下文切換的方式來執(zhí)行不同的 Block 。

有趣的是蛆楞, GCD 提供給你至少五個(gè)特定的隊(duì)列溯乒,可根據(jù)隊(duì)列類型選擇使用。

Queue Types 隊(duì)列類型

首先臊岸,系統(tǒng)提供給你一個(gè)叫做 主隊(duì)列(main queue) 的特殊隊(duì)列橙数。和其它串行隊(duì)列一樣,這個(gè)隊(duì)列中的任務(wù)一次只能執(zhí)行一個(gè)帅戒。然而灯帮,它能保證所有的任務(wù)都在主線程執(zhí)行,而主線程是唯一可用于更新 UI 的線程逻住。這個(gè)隊(duì)列就是用于發(fā)生消息給 UIView 或發(fā)送通知的钟哥。

系統(tǒng)同時(shí)提供給你好幾個(gè)并發(fā)隊(duì)列。它們叫做 全局調(diào)度隊(duì)列(Global Dispatch Queues) 瞎访。目前的四個(gè)全局隊(duì)列有著不同的優(yōu)先級:background腻贰、low、default 以及 high扒秸。要知道播演,Apple 的 API 也會使用這些隊(duì)列冀瓦,所以你添加的任何任務(wù)都不會是這些隊(duì)列中唯一的任務(wù)。

最后写烤,你也可以創(chuàng)建自己的串行隊(duì)列或并發(fā)隊(duì)列翼闽。這就是說,至少有五個(gè)隊(duì)列任你處置:主隊(duì)列洲炊、四個(gè)全局調(diào)度隊(duì)列感局,再加上任何你自己創(chuàng)建的隊(duì)列。

以上是調(diào)度隊(duì)列的大框架暂衡!


dispatch queue分成以下三種:

1)運(yùn)行在主線程的Main queue询微,通過dispatch_get_main_queue獲取。

復(fù)制代碼

/*!

* @function dispatch_get_main_queue

*

* @abstract

* Returns the default queue that is bound to the main thread.

*

* @discussion

* In order to invoke blocks submitted to the main queue, the application must

* call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main

* thread.

*

* @result

* Returns the main queue. This queue is created automatically on behalf of

* the main thread before main() is called.

*/

__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)

DISPATCH_EXPORT struct dispatch_queue_s _dispatch_main_q;

#define dispatch_get_main_queue() \

DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q)

復(fù)制代碼

可以看出狂巢,dispatch_get_main_queue也是一種dispatch_queue_t撑毛。

2)并行隊(duì)列g(shù)lobal dispatch queue,通過dispatch_get_global_queue獲取隧膘,由系統(tǒng)創(chuàng)建三個(gè)不同優(yōu)先級的dispatch queue代态。并行隊(duì)列的執(zhí)行順序與其加入隊(duì)列的順序相同。

3)串行隊(duì)列serial queues一般用于按順序同步訪問疹吃,可創(chuàng)建任意數(shù)量的串行隊(duì)列蹦疑,各個(gè)串行隊(duì)列之間是并發(fā)的。

當(dāng)想要任務(wù)按照某一個(gè)特定的順序執(zhí)行時(shí)萨驶,串行隊(duì)列是很有用的歉摧。串行隊(duì)列在同一個(gè)時(shí)間只執(zhí)行一個(gè)任務(wù)。我們可以使用串行隊(duì)列代替鎖去保護(hù)共享的數(shù)據(jù)腔呜。和鎖不同叁温,一個(gè)串行隊(duì)列可以保證任務(wù)在一個(gè)可預(yù)知的順序下執(zhí)行。

serial queues通過dispatch_queue_create創(chuàng)建核畴,可以使用函數(shù)dispatch_retain和dispatch_release去增加或者減少引用計(jì)數(shù)膝但。

GCD的用法:

復(fù)制代碼

//? 后臺執(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

});

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é)果

});

復(fù)制代碼

一個(gè)應(yīng)用GCD的例子:

復(fù)制代碼

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];

NSError * error;

NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];

if (data != nil) {

dispatch_async(dispatch_get_main_queue(), ^{

NSLog(@"call back, the data is: %@", data);

});

} else {

NSLog(@"error when download:%@", error);

}

});

復(fù)制代碼


Dispatch Groups(調(diào)度組)

Dispatch Group 會在整個(gè)組的任務(wù)都完成時(shí)通知你。這些任務(wù)可以是同步的谤草,也可以是異步的跟束,即便在不同的隊(duì)列也行。而且在整個(gè)組的任務(wù)都完成時(shí)丑孩,Dispatch Group 可以用同步的或者異步的方式通知你冀宴。因?yàn)橐O(jiān)控的任務(wù)在不同隊(duì)列,那就用一個(gè) dispatch_group_t 的實(shí)例來記下這些不同的任務(wù)温学。

當(dāng)組中所有的事件都完成時(shí)略贮,GCD 的 API 提供了兩種通知方式。

第一種是 dispatch_group_wait ,它會阻塞當(dāng)前線程逃延,直到組里面所有的任務(wù)都完成或者等到某個(gè)超時(shí)發(fā)生览妖。這恰好是你目前所需要的。

打開 PhotoManager.m真友,用下列實(shí)現(xiàn)替換 downloadPhotosWithCompletionBlock:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock

{

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1

__block NSError *error;

dispatch_group_t downloadGroup = dispatch_group_create(); // 2

for (NSInteger i = 0; i < 3; i++) {

NSURL *url;

switch (i) {

case 0:

url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];

break;

case 1:

url = [NSURL URLWithString:kSuccessKidURLString];

break;

case 2:

url = [NSURL URLWithString:kLotsOfFacesURLString];

break;

default:

break;

}

dispatch_group_enter(downloadGroup); // 3

Photo *photo = [[Photo alloc] initwithURL:url

withCompletionBlock:^(UIImage *image, NSError *_error) {

if (_error) {

error = _error;

}

dispatch_group_leave(downloadGroup); // 4

}];

[[PhotoManager sharedManager] addPhoto:photo];

}

dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5

dispatch_async(dispatch_get_main_queue(), ^{ // 6

if (completionBlock) { // 7

completionBlock(error);

}

});

});

}

按照注釋的順序黄痪,你會看到:

1. 因?yàn)槟阍谑褂玫氖峭降?dispatch_group_wait ,它會阻塞當(dāng)前線程盔然,所以你要用 dispatch_async 將整個(gè)方法放入后臺隊(duì)列以避免阻塞主線程。

2. 創(chuàng)建一個(gè)新的 Dispatch Group是嗜,它的作用就像一個(gè)用于未完成任務(wù)的計(jì)數(shù)器愈案。

3. dispatch_group_enter 手動(dòng)通知 Dispatch Group 任務(wù)已經(jīng)開始。你必須保證 dispatch_group_enter 和 dispatch_group_leave 成對出現(xiàn)鹅搪,否則你可能會遇到詭異的崩潰問題站绪。

4. 手動(dòng)通知 Group 它的工作已經(jīng)完成。再次說明丽柿,你必須要確保進(jìn)入 Group 的次數(shù)和離開 Group 的次數(shù)相等恢准。

5. dispatch_group_wait 會一直等待,直到任務(wù)全部完成或者超時(shí)甫题。如果在所有任務(wù)完成前超時(shí)了馁筐,該函數(shù)會返回一個(gè)非零值。你可以對此返回值做條件判斷以確定是否超出等待周期坠非;然而敏沉,你在這里用 DISPATCH_TIME_FOREVER 讓它永遠(yuǎn)等待。它的意思炎码,勿庸置疑就是盟迟,永-遠(yuǎn)-等-待!這樣很好潦闲,因?yàn)閳D片的創(chuàng)建工作總是會完成的攒菠。

6. 此時(shí)此刻,你已經(jīng)確保了歉闰,要么所有的圖片任務(wù)都已完成辖众,要么發(fā)生了超時(shí)。然后新娜,你在主線程上運(yùn)行 completionBlock 回調(diào)赵辕。這會將工作放到主線程上,并在稍后執(zhí)行概龄。

7. 最后还惠,檢查 completionBlock 是否為 nil,如果不是私杜,那就運(yùn)行它蚕键。

編譯并運(yùn)行你的應(yīng)用救欧,嘗試下載多個(gè)圖片,觀察你的應(yīng)用是在何時(shí)運(yùn)行 completionBlock 的锣光。

注意:如果你是在真機(jī)上運(yùn)行應(yīng)用笆怠,而且網(wǎng)絡(luò)活動(dòng)發(fā)生得太快以致難以觀察 completionBlock 被調(diào)用的時(shí)刻,那么你可以在 Settings 應(yīng)用里的開發(fā)者相關(guān)部分里打開一些網(wǎng)絡(luò)設(shè)置誊爹,以確保代碼按照我們所期望的那樣工作蹬刷。只需去往 Network Link Conditioner 區(qū),開啟它频丘,再選擇一個(gè) Profile办成,“Very Bad Network” 就不錯(cuò)。

如果你是在模擬器里運(yùn)行應(yīng)用搂漠,你可以使用 來自 GitHub 的 Network Link Conditioner 來改變網(wǎng)絡(luò)速度迂卢。它會成為你工具箱中的一個(gè)好工具,因?yàn)樗鼜?qiáng)制你研究你的應(yīng)用在連接速度并非最佳的情況下會變成什么樣桐汤。

目前為止的解決方案還不錯(cuò)而克,但是總體來說,如果可能怔毛,最好還是要避免阻塞線程员萍。你的下一個(gè)任務(wù)是重寫一些方法,以便當(dāng)所有下載任務(wù)完成時(shí)能異步通知你馆截。

在我們轉(zhuǎn)向另外一種使用 Dispatch Group 的方式之前充活,先看一個(gè)簡要的概述,關(guān)于何時(shí)以及怎樣使用有著不同的隊(duì)列類型的 Dispatch Group :

1. 自定義串行隊(duì)列:它很適合當(dāng)一組任務(wù)完成時(shí)發(fā)出通知蜡娶。

2. 主隊(duì)列(串行):它也很適合這樣的情況混卵。但如果你要同步地等待所有工作地完成,那你就不應(yīng)該使用它窖张,因?yàn)槟悴荒茏枞骶€程幕随。然而,異步模型是一個(gè)很有吸引力的能用于在幾個(gè)較長任務(wù)(例如網(wǎng)絡(luò)調(diào)用)完成后更新 UI 的方式宿接。

3. 并發(fā)隊(duì)列:它也很適合 Dispatch Group 和完成時(shí)通知赘淮。

Dispatch Group,第二種方式

上面的一切都很好睦霎,但在另一個(gè)隊(duì)列上異步調(diào)度然后使用 dispatch_group_wait 來阻塞實(shí)在顯得有些笨拙梢卸。是的,還有另一種方式……

在 PhotoManager.m 中找到 downloadPhotosWithCompletionBlock: 方法副女,用下面的實(shí)現(xiàn)替換它:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock

{

// 1

__block NSError *error;

dispatch_group_t downloadGroup = dispatch_group_create();

for (NSInteger i = 0; i < 3; i++) {

NSURL *url;

switch (i) {

case 0:

url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];

break;

case 1:

url = [NSURL URLWithString:kSuccessKidURLString];

break;

case 2:

url = [NSURL URLWithString:kLotsOfFacesURLString];

break;

default:

break;

}

dispatch_group_enter(downloadGroup); // 2

Photo *photo = [[Photo alloc] initwithURL:url

withCompletionBlock:^(UIImage *image, NSError *_error) {

if (_error) {

error = _error;

}

dispatch_group_leave(downloadGroup); // 3

}];

[[PhotoManager sharedManager] addPhoto:photo];

}

dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4

if (completionBlock) {

completionBlock(error);

}

});

}

下面解釋新的異步方法如何工作:

1. 在新的實(shí)現(xiàn)里蛤高,因?yàn)槟銢]有阻塞主線程,所以你并不需要將方法包裹在 async 調(diào)用中。

2. 同樣的 enter 方法戴陡,沒做任何修改塞绿。

3. 同樣的 leave 方法,也沒做任何修改恤批。

4. dispatch_group_notify 以異步的方式工作异吻。當(dāng) Dispatch Group 中沒有任何任務(wù)時(shí),它就會執(zhí)行其代碼喜庞,那么 completionBlock 便會運(yùn)行诀浪。你還指定了運(yùn)行 completionBlock 的隊(duì)列,此處赋荆,主隊(duì)列就是你所需要的笋妥。

對于這個(gè)特定的工作,上面的處理明顯更清晰窄潭,而且也不會阻塞任何線程。




A B C D 4個(gè)并發(fā)下載任務(wù)酵颁,怎樣在第一時(shí)間知道任務(wù)全部完成嫉你?

dispatch_group 可以幫我們實(shí)現(xiàn)這樣的控制。

上代碼躏惋,看說明.

[objc] view plain copy

dispatch_group_t group = dispatch_group_create();

// 某個(gè)任務(wù)放進(jìn) group

dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{

// 任務(wù)代碼1

});

dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{

// 任務(wù)代碼2

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

// 任務(wù)全部完成處理

NSLog(@"isover");

});

創(chuàng)建一個(gè)任務(wù)組幽污,然后將異步操作放進(jìn)組里面,在最后用notify 告知所有任務(wù)完成簿姨,并做相應(yīng)處理距误,一般來說都是在主線程里面刷新UI來提示用戶了。你如果不依賴UI放進(jìn)子線程里面也是沒有問題的扁位。當(dāng)然group同步的方式還有其他

[objc] view plain copy

dispatch_async(dispatch_get_global_queue(0, 0), ^{

for (int i = 0; i < 3; i ++)

{

dispatch_group_enter(group);

// 任務(wù)代碼i 假定任務(wù) 是異步執(zhí)行block回調(diào)

// block 回調(diào)執(zhí)行

dispatch_group_leave(group);

// block 回調(diào)執(zhí)行

}

});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_async(dispatch_get_main_queue(), ^{

// 主線程處理

});

首先我們異步執(zhí)行准潭,因?yàn)閐ispatch_group_wait函數(shù)是阻塞的,for里面安排了三個(gè)任務(wù)域仇,這三個(gè)任務(wù)都是加載刑然,在任務(wù)開始前 調(diào)用 enter,任務(wù)完成時(shí)調(diào)用leave暇务,wait函數(shù)一直阻塞泼掠,直到它發(fā)現(xiàn)group里面的任務(wù)全部leave,它才放棄阻塞(任務(wù)全部完成),然后我們在主線程更新UI告知用戶


7.3垦细、定時(shí)器

大多數(shù)情況下择镇,對于定時(shí)事件,你會選擇NSTimer括改。定時(shí)器的GCD版本是底層的腻豌,它會給你更多控制權(quán)——但要小心使用。

需要特別重點(diǎn)指出的是,為了讓OS節(jié)省電量饲梭,需要為GCD的定時(shí)器接口指定一個(gè)低的誤差值乘盖。如果你不必要的指定了一個(gè)過低的誤差值,你將會浪費(fèi)更多的電量憔涉。

這里我們設(shè)定了一個(gè)5秒的定時(shí)器订框,并允許有十分之一秒的誤差:

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,

0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);

dispatch_source_set_event_handler(source, ^(){

NSLog(@"Time flies.");

});

dispatch_time_t start

dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC,

100ull * NSEC_PER_MSEC);

self.source = source;

dispatch_resume(self.source);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市兜叨,隨后出現(xiàn)的幾起案子穿扳,更是在濱河造成了極大的恐慌,老刑警劉巖国旷,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛物,死亡現(xiàn)場離奇詭異,居然都是意外死亡跪但,警方通過查閱死者的電腦和手機(jī)履羞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屡久,“玉大人忆首,你說我怎么就攤上這事”换罚” “怎么了糙及?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長筛欢。 經(jīng)常有香客問我浸锨,道長,這世上最難降的妖魔是什么版姑? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任柱搜,我火速辦了婚禮,結(jié)果婚禮上漠酿,老公的妹妹穿的比我還像新娘冯凹。我一直安慰自己,他們只是感情好炒嘲,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布宇姚。 她就那樣靜靜地躺著,像睡著了一般夫凸。 火紅的嫁衣襯著肌膚如雪浑劳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天夭拌,我揣著相機(jī)與錄音魔熏,去河邊找鬼衷咽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒜绽,可吹牛的內(nèi)容都是我干的镶骗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼躲雅,長吁一口氣:“原來是場噩夢啊……” “哼鼎姊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起相赁,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤相寇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后钮科,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唤衫,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年绵脯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了佳励。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛆挫,死狀恐怖植兰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情璃吧,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布废境,位于F島的核電站畜挨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏噩凹。R本人自食惡果不足惜巴元,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驮宴。 院中可真熱鬧逮刨,春花似錦、人聲如沸堵泽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迎罗。三九已至睬愤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纹安,已是汗流浹背尤辱。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工砂豌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人光督。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓阳距,卻偏偏與公主長得像,于是被迫代替她去往敵國和親结借。 傳聞我的和親對象是個(gè)殘疾皇子筐摘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容