GCD入門(一)基本概念和Dispatch Queue

轉(zhuǎn)自此處

什么是GCD?

Grand Central Dispatch或者GCD,是一套低層API,提供了一種新的方法來進(jìn)行并發(fā)程序編寫绿满。從基本功能上講,GCD有點(diǎn)像NSOperationQueue窟扑,他們都允許程序?qū)⑷蝿?wù)切分為多個單一任務(wù)然后提交至工作隊(duì)列來并發(fā)地或者串行地執(zhí)行喇颁。GCD比之NSOpertionQueue更底層更高效漏健,并且它不是Cocoa框架的一部分。

除了代碼的平行執(zhí)行能力橘霎,GCD還提供高度集成的事件控制系統(tǒng)蔫浆。可以設(shè)置句柄來響應(yīng)文件描述符姐叁、mach ports(Mach port 用于 OS X上的進(jìn)程間通訊)瓦盛、進(jìn)程、計(jì)時器外潜、信號原环、用戶生成事件。這些句柄通過GCD來并發(fā)執(zhí)行处窥。

GCD的API很大程度上基于block嘱吗,當(dāng)然,GCD也可以脫離block來使用滔驾,比如使用傳統(tǒng)c機(jī)制提供函數(shù)指針和上下文指針谒麦。實(shí)踐證明,當(dāng)配合block使用時哆致,GCD非常簡單易用且能發(fā)揮其最大能力绕德。

你可以在Mac上敲命令“man dispatch”來獲取GCD的文檔。

為何使用?

GCD提供很多超越傳統(tǒng)多線程編程的優(yōu)勢:

易用: GCD比之thread跟簡單易用沽瞭。由于GCD基于work unit而非像thread那樣基于運(yùn)算迁匠,所以GCD可以控制諸如等待任務(wù)結(jié)束剩瓶、監(jiān)視文件描述符驹溃、周期執(zhí)行代碼以及工作掛起等任務(wù)⊙邮铮基于block的血統(tǒng)導(dǎo)致它能極為簡單得在不同代碼作用域之間傳遞上下文豌鹤。

效率: GCD被實(shí)現(xiàn)得如此輕量和優(yōu)雅,使得它在很多地方比之專門創(chuàng)建消耗資源的線程更實(shí)用且快速枝缔。這關(guān)系到易用性:導(dǎo)致GCD易用的原因有一部分在于你可以不用擔(dān)心太多的效率問題而僅僅使用它就行了布疙。

性能: GCD自動根據(jù)系統(tǒng)負(fù)載來增減線程數(shù)量,這就減少了上下文切換以及增加了計(jì)算效率愿卸。

Dispatch Objects

盡管GCD是純c語言的灵临,但它被組建成面向?qū)ο蟮娘L(fēng)格。GCD對象被稱為dispatch object趴荸。Dispatch object像Cocoa對象一樣是引用計(jì)數(shù)的儒溉。使用dispatch_release和dispatch_retain函數(shù)來操作dispatch object的引用計(jì)數(shù)來進(jìn)行內(nèi)存管理。但主意不像Cocoa對象发钝,dispatch object并不參與垃圾回收系統(tǒng)顿涣,所以即使開啟了GC波闹,你也必須手動管理GCD對象的內(nèi)存。

Dispatch queues 和 dispatch sources(后面會介紹到)可以被掛起和恢復(fù)涛碑,可以有一個相關(guān)聯(lián)的任意上下文指針精堕,可以有一個相關(guān)聯(lián)的任務(wù)完成觸發(fā)函數(shù)∑颜希可以查閱“man dispatch_object”來獲取這些功能的更多信息歹篓。

Dispatch Queues

GCD的基本概念就是dispatch queue。dispatch queue是一個對象晌涕,它可以接受任務(wù)滋捶,并將任務(wù)以先到先執(zhí)行的順序來執(zhí)行。dispatch queue可以是并發(fā)的或串行的余黎。并發(fā)任務(wù)會像NSOperationQueue那樣基于系統(tǒng)負(fù)載來合適地并發(fā)進(jìn)行重窟,串行隊(duì)列同一時間只執(zhí)行單一任務(wù)。

GCD中有三種隊(duì)列類型:

  • The main queue:

與主線程功能相同惧财。實(shí)際上巡扇,提交至main queue的任務(wù)會在主線程中執(zhí)行。main queue可以調(diào)用dispatch_get_main_queue()來獲得垮衷。因?yàn)閙ain queue是與主線程相關(guān)的厅翔,所以這是一個串行隊(duì)列。

  • Global queues:

全局隊(duì)列是并發(fā)隊(duì)列搀突,并由整個進(jìn)程共享刀闷。進(jìn)程中存在三個全局隊(duì)列:高、中(默認(rèn))仰迁、低三個優(yōu)先級隊(duì)列甸昏。可以調(diào)用dispatch_get_global_queue函數(shù)傳入優(yōu)先級來訪問隊(duì)列徐许。

  • 用戶隊(duì)列: 用戶隊(duì)列

(GCD并不這樣稱呼這種隊(duì)列, 但是沒有一個特定的名字來形容這種隊(duì)列施蜜,所以我們稱其為用戶隊(duì)列) 是用函數(shù) dispatch_queue_create 創(chuàng)建的隊(duì)列. 這些隊(duì)列是串行的。正因?yàn)槿绱舜朴纾鼈兛梢杂脕硗瓿赏綑C(jī)制, 有點(diǎn)像傳統(tǒng)線程中的mutex翻默。

創(chuàng)建隊(duì)列

要使用用戶隊(duì)列,我們首先得創(chuàng)建一個恰起。調(diào)用函數(shù)dispatch_queue_create就行了修械。函數(shù)的第一個參數(shù)是一個標(biāo)簽,這純是為了debug检盼。Apple建議我們使用倒置域名來命名隊(duì)列肯污,比如“com.dreamingwish.subsystem.task”。這些名字會在崩潰日志中被顯示出來,也可以被調(diào)試器調(diào)用仇箱,這在調(diào)試中會很有用县恕。第二個參數(shù)目前還不支持,傳入NULL就行了剂桥。

提交 Job

向一個隊(duì)列提交Job很簡單:調(diào)用dispatch_async函數(shù)忠烛,傳入一個隊(duì)列和一個block。隊(duì)列會在輪到這個block執(zhí)行時執(zhí)行這個block的代碼权逗。下面的例子是一個在后臺執(zhí)行一個巨長的任務(wù):

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self goDoSomethingLongAndInvolved];
        NSLog(@"Done doing something long and involved");
});

dispatch_async 函數(shù)會立即返回, block會在后臺異步執(zhí)行美尸。

當(dāng)然,通常斟薇,任務(wù)完成時簡單地NSLog個消息不是個事兒师坎。在典型的Cocoa程序中,你很有可能希望在任務(wù)完成時更新界面堪滨,這就意味著需要在主線程中執(zhí)行一些代碼胯陋。你可以簡單地完成這個任務(wù)——使用嵌套的dispatch,在外層中執(zhí)行后臺任務(wù)袱箱,在內(nèi)層中將任務(wù)dispatch到main queue:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self goDoSomethingLongAndInvolved];
        dispatch_async(dispatch_get_main_queue(), ^{
            [textField setStringValue:@"Done doing something long and involved"];
        });
});

還有一個函數(shù)叫dispatch_sync遏乔,它干的事兒和dispatch_async相同,但是它會等待block中的代碼執(zhí)行完成并返回发笔。結(jié)合 __block類型修飾符盟萨,可以用來從執(zhí)行中的block獲取一個值。例如了讨,你可能有一段代碼在后臺執(zhí)行捻激,而它需要從界面控制層獲取一個值。那么你可以使用dispatch_sync簡單辦到:

__block NSString *stringValue;
dispatch_sync(dispatch_get_main_queue(), ^{
        // __block variables aren't automatically retained
        // so we'd better make sure we have a reference we can keep
        stringValue = [[textField stringValue] copy];
});
[stringValue autorelease];
// use stringValue in the background now

我們還可以使用更好的方法來完成這件事——使用更“異步”的風(fēng)格前计。不同于取界面層的值時要阻塞后臺線程胞谭,你可以使用嵌套的block來中止后臺線程,然后從主線程中獲取值残炮,然后再將后期處理提交至后臺線程:

    dispatch_queue_t bgQueue = myQueue;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSString *stringValue = [[[textField stringValue] copy] autorelease];
        dispatch_async(bgQueue, ^{
            // use stringValue in the background now
        });
    });

取決于你的需求韭赘,myQueue可以是用戶隊(duì)列也可以使全局隊(duì)列缩滨。

不再使用鎖(Lock)

用戶隊(duì)列可以用于替代鎖來完成同步機(jī)制势就。在傳統(tǒng)多線程編程中,你可能有一個對象要被多個線程使用脉漏,你需要一個鎖來保護(hù)這個對象

    NSLock *lock;

訪問代碼會像這樣:

- (id)something
{
    id localSomething;
    [lock lock];
    localSomething = [[something retain] autorelease];
    [lock unlock];
    return localSomething;
}
 
- (void)setSomething:(id)newSomething
{
    [lock lock];
    if(newSomething != something)
    {
        [something release];
        something = [newSomething retain];
        [self updateSomethingCaches];
    }
    [lock unlock];
}

使用GCD苞冯,可以使用queue來替代:

dispatch_queue_t queue;

要用于同步機(jī)制,queue必須是一個用戶隊(duì)列(從OS X v10.7和iOS 4.3開始侧巨,還必須指定為DISPATCH_QUEUE_SERIAL)舅锄,而非全局隊(duì)列,所以使用dispatch_queue_create初始化一個司忱。然后可以用dispatch_async 或者 dispatch_sync將共享數(shù)據(jù)的訪問代碼封裝起來:

- (id)something
{
    __block id localSomething;
    dispatch_sync(queue, ^{
        localSomething = [something retain];
    });
    return [localSomething autorelease];
}
 
- (void)setSomething:(id)newSomething
{
    dispatch_async(queue, ^{
        if(newSomething != something)
        {
            [something release];
            something = [newSomething retain];
            [self updateSomethingCaches];
        }
    });
}

值得注意的是dispatch queue是非常輕量級的皇忿,所以你可以大用特用畴蹭,就像你以前使用lock一樣。

現(xiàn)在你可能要問:“這樣很好鳍烁,但是有意思嗎叨襟?我就是換了點(diǎn)代碼辦到了同一件事兒♂;模”

實(shí)際上糊闽,使用GCD途徑有幾個好處:

平行計(jì)算: 注意在第二個版本的代碼中, -setSomething:是怎么使用dispatch_async的爹梁。調(diào)用 -setSomething:會立即返回右犹,然后這一大堆工作會在后臺執(zhí)行。如果updateSomethingCaches是一個很費(fèi)時費(fèi)力的任務(wù)姚垃,且調(diào)用者將要進(jìn)行一項(xiàng)處理器高負(fù)荷任務(wù)念链,那么這樣做會很棒。

安全: 使用GCD积糯,我們就不可能意外寫出具有不成對Lock的代碼钓账。在常規(guī)Lock代碼中,我們很可能在解鎖之前讓代碼返回了絮宁。使用GCD梆暮,隊(duì)列通常持續(xù)運(yùn)行,你必將歸還控制權(quán)绍昂。

控制: 使用GCD我們可以掛起和恢復(fù)dispatch queue啦粹,而這是基于鎖的方法所不能實(shí)現(xiàn)的。我們還可以將一個用戶隊(duì)列指向另一個dspatch queue窘游,使得這個用戶隊(duì)列繼承那個dispatch queue的屬性唠椭。使用這種方法,隊(duì)列的優(yōu)先級可以被調(diào)整——通過將該隊(duì)列指向一個不同的全局隊(duì)列忍饰,若有必要的話贪嫂,這個隊(duì)列甚至可以被用來在主線程上執(zhí)行代碼。

集成: GCD的事件系統(tǒng)與dispatch queue相集成艾蓝。對象需要使用的任何事件或者計(jì)時器都可以從該對象的隊(duì)列中指向力崇,使得這些句柄可以自動在該隊(duì)列上執(zhí)行,從而使得句柄可以與對象自動同步赢织。

總結(jié)

現(xiàn)在你已經(jīng)知道了GCD的基本概念亮靴、怎樣創(chuàng)建dispatch queue、怎樣提交Job至dispatch queue以及怎樣將隊(duì)列用作線程同步于置。接下來我會向你展示如何使用GCD來編寫平行執(zhí)行代碼來充分利用多核系統(tǒng)的性能^ ^茧吊。我還會討論GCD更深層的東西,包括事件系統(tǒng)和queue targeting。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搓侄,一起剝皮案震驚了整個濱河市瞄桨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讶踪,老刑警劉巖讲婚,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俊柔,居然都是意外死亡筹麸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門雏婶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來物赶,“玉大人,你說我怎么就攤上這事留晚〗妥希” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵错维,是天一觀的道長奖地。 經(jīng)常有香客問我,道長赋焕,這世上最難降的妖魔是什么参歹? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮隆判,結(jié)果婚禮上犬庇,老公的妹妹穿的比我還像新娘。我一直安慰自己侨嘀,他們只是感情好臭挽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咬腕,像睡著了一般欢峰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涨共,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天纽帖,我揣著相機(jī)與錄音,去河邊找鬼煞赢。 笑死抛计,一個胖子當(dāng)著我的面吹牛哄孤,可吹牛的內(nèi)容都是我干的照筑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凝危!你這毒婦竟也來了波俄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蛾默,失蹤者是張志新(化名)和其女友劉穎懦铺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體支鸡,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冬念,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了牧挣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片急前。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瀑构,靈堂內(nèi)的尸體忽然破棺而出裆针,到底是詐尸還是另有隱情,我是刑警寧澤寺晌,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布世吨,位于F島的核電站,受9級特大地震影響呻征,放射性物質(zhì)發(fā)生泄漏耘婚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一陆赋、第九天 我趴在偏房一處隱蔽的房頂上張望边篮。 院中可真熱鬧,春花似錦奏甫、人聲如沸戈轿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽思杯。三九已至,卻和暖如春挠进,著一層夾襖步出監(jiān)牢的瞬間色乾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工领突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暖璧,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓君旦,卻偏偏與公主長得像澎办,于是被迫代替她去往敵國和親嘲碱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

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