Swift - 多線程實(shí)現(xiàn)方式 - Grand Central Dispatch(GCD)

1,Swift繼續(xù)使用Object-C原有的一套線程炎码,包括三種多線程編程技術(shù):

(1)Thread

(2)Cocoa Operation(Operation和OperationQueue)

(3)Grand Central Dispath(GCD)

2涕俗,本文著重介紹Grand Central Dispath(GCD)

GCD是Apple開(kāi)發(fā)的一個(gè)多核編程的解決方法痰腮,基本概念就是dispatch queue(調(diào)度隊(duì)列)赋访,queue是一個(gè)對(duì)象,它可以接受任務(wù)悟民,并將任務(wù)以先到先執(zhí)行的順序來(lái)執(zhí)行坝辫。dispatch queue可以是并發(fā)的或串行的篷就。GCD的底層依然是用線程實(shí)現(xiàn)射亏,不過(guò)我們可以不用關(guān)注實(shí)現(xiàn)的細(xì)節(jié)。其優(yōu)點(diǎn)有如下幾點(diǎn):

(1)易用:GCD比thread更簡(jiǎn)單易用竭业≈侨螅基于block的特效使它能極為簡(jiǎn)單地在不同代碼作用域之間傳遞上下文。

(2)效率:GCD實(shí)現(xiàn)功能輕量未辆,優(yōu)雅窟绷,使得它在很多地方比專門創(chuàng)建消耗資源的線程更加實(shí)用且快捷。

(3)性能:GCD自動(dòng)根據(jù)系統(tǒng)負(fù)載來(lái)增減線程數(shù)量咐柜,從而減少了上下文切換并增加了計(jì)算效率兼蜈。

(4)安全:無(wú)需加鎖或其他同步機(jī)制。

本文代碼已全部更新至Swift3拙友。


3为狸,GCD三種創(chuàng)建隊(duì)列的方法

(1)自己創(chuàng)建一個(gè)隊(duì)列

第一個(gè)參數(shù)代表隊(duì)列的名稱,可以任意起名

第二個(gè)參數(shù)代表隊(duì)列屬于串行還是并行執(zhí)行任務(wù)

串行隊(duì)列一次只執(zhí)行一個(gè)任務(wù)遗契。一般用于按順序同步訪問(wèn)辐棒,但我們可以創(chuàng)建任意數(shù)量的串行隊(duì)列,各個(gè)串行隊(duì)列之間是并發(fā)的牍蜂。

并行隊(duì)列的執(zhí)行順序與其加入隊(duì)列的順序相同漾根。可以并發(fā)執(zhí)行多個(gè)任務(wù)鲫竞,但是執(zhí)行完成的順序是隨機(jī)的辐怕。

//創(chuàng)建串行隊(duì)列l(wèi)et serial = DispatchQueue(label:"serialQueue1")

//創(chuàng)建并行隊(duì)列l(wèi)et concurrent = DispatchQueue(label:"concurrentQueue1", attributes: .concurrent)

(2)獲取系統(tǒng)存在的全局隊(duì)列?

Global Dispatch Queue有4個(gè)執(zhí)行優(yōu)先級(jí):

.userInitiated??高

.default??正常

.utility??低

.background?非常低的優(yōu)先級(jí)(這個(gè)優(yōu)先級(jí)只用于不太關(guān)心完成時(shí)間的真正的后臺(tái)任務(wù))


let globalQueue = DispatchQueue.global(qos: .default)

(3)運(yùn)行在主線程的Main Dispatch Queue?

正如名稱中的Main一樣,這是在主線程里執(zhí)行的隊(duì)列从绘。因?yàn)橹骶€程只有一個(gè)寄疏,所有這自然是串行隊(duì)列其做。一起跟UI有關(guān)的操作必須放在主線程中執(zhí)行。

let mainQueue = DispatchQueue.main

4赁还,添加任務(wù)到隊(duì)列的兩種方法?

(1)async異步追加Block塊(async函數(shù)不做任何等待)

DispatchQueue.global(qos: .default).async {

? ? //處理耗時(shí)操作的代碼塊...print("do work")


? ? //操作完成妖泄,調(diào)用主線程來(lái)刷新界面? ? DispatchQueue.main.async {

? ? ? ? print("main refresh")

? ? }

}

(2)sync同步追加Block塊?

同步追加Block塊,與上面相反艘策。在追加Block結(jié)束之前蹈胡,sync函數(shù)會(huì)一直等待,等待隊(duì)列前面的所有任務(wù)完成后才能執(zhí)行追加的任務(wù)朋蔫。

//添加同步代碼塊到global隊(duì)列//不會(huì)造成死鎖罚渐,但會(huì)一直等待代碼塊執(zhí)行完畢DispatchQueue.global(qos: .default).sync {

? ? print("sync1")

}

print("end1")

//添加同步代碼塊到main隊(duì)列//會(huì)引起死鎖//因?yàn)樵谥骶€程里面添加一個(gè)任務(wù),因?yàn)槭峭窖蓖砸忍砑拥娜蝿?wù)執(zhí)行完畢后才能繼續(xù)走下去荷并。但是新添加的任務(wù)排在//隊(duì)列的末尾,要執(zhí)行完成必須等前面的任務(wù)執(zhí)行完成青扔,由此又回到了第一步源织,程序卡死DispatchQueue.main.sync {

? ? print("sync2")

}

print("end2")

5,暫臀⒉或者繼續(xù)隊(duì)列

這兩個(gè)函數(shù)是異步的谈息,而且只在不同的blocks之間生效,對(duì)已經(jīng)正在執(zhí)行的任務(wù)沒(méi)有影響凛剥。

suspend()后侠仇,追加到Dispatch Queue中尚未執(zhí)行的任務(wù)在此之后停止執(zhí)行。

而resume()則使得這些任務(wù)能夠繼續(xù)執(zhí)行犁珠。

//創(chuàng)建并行隊(duì)列l(wèi)et conQueue = DispatchQueue(label:"concurrentQueue1", attributes: .concurrent)//暫停一個(gè)隊(duì)列conQueue.suspend()//繼續(xù)隊(duì)列conQueue.resume()

6逻炊,只執(zhí)行一次

過(guò)去dispatch_once中的代碼塊在應(yīng)用程序里面只執(zhí)行一次,無(wú)論是不是多線程犁享。因此其可以用來(lái)實(shí)現(xiàn)單例模式余素,安全,簡(jiǎn)潔饼疙,方便溺森。

//往dispatch_get_global_queue隊(duì)列中添加代碼塊,只執(zhí)行一次var predicate:dispatch_once_t =0dispatch_once(&predicate, { () -> Voidin//只執(zhí)行一次窑眯,可用于創(chuàng)建單例println("work")

})

在Swift3中屏积,dispatch_once被廢棄了,我們要替換成其他全局或者靜態(tài)變量和常量.

privatevar once1:Void = {

? ? //只執(zhí)行一次print("once1")

}()

privatelazy var once2:String = {

? ? //只執(zhí)行一次磅甩,可用于創(chuàng)建單例print("once2")

? ? return"once2"}()

7炊林,asyncAfter?延遲調(diào)用

asyncAfter 并不是在指定時(shí)間后執(zhí)行任務(wù)處理,而是在指定時(shí)間后把任務(wù)追加到queue里面卷要。因此會(huì)有少許延遲渣聚。注意独榴,我們不能(直接)取消我們已經(jīng)提交到 asyncAfter 里的代碼。

//延時(shí)2秒執(zhí)行DispatchQueue.global(qos: .default).asyncAfter(deadline: DispatchTime.now() +2.0) {

? ? print("after!")

}

如果需要取消正在等待執(zhí)行的Block操作奕枝,我們可以先將這個(gè)Block封裝到DispatchWorkItem對(duì)象中棺榔,然后對(duì)其發(fā)送cancle,來(lái)取消一個(gè)正在等待執(zhí)行的block隘道。

//將要執(zhí)行的操作封裝到DispatchWorkItem中l(wèi)et task = DispatchWorkItem { print("after!") }//延時(shí)2秒執(zhí)行DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() +2, execute: task)//取消任務(wù)task.cancel()

8症歇,多個(gè)任務(wù)全部結(jié)束后做一個(gè)全部結(jié)束的處理

async(group:):用來(lái)監(jiān)視一組block對(duì)象的完成,你可以同步或異步地監(jiān)視

notify():用來(lái)匯總結(jié)果谭梗,所有任務(wù)結(jié)束匯總忘晤,不阻塞當(dāng)前線程

wait():等待直到所有任務(wù)執(zhí)行結(jié)束,中途不能取消激捏,阻塞當(dāng)前線程

//獲取系統(tǒng)存在的全局隊(duì)列l(wèi)et queue = DispatchQueue.global(qos: .default)//定義一個(gè)grouplet group = DispatchGroup()//并發(fā)任務(wù)设塔,順序執(zhí)行queue.async(group: group) {

? ? sleep(2)

? ? print("block1")

}

queue.async(group: group) {

? ? print("block2")

}

queue.async(group: group) {

? ? print("block3")

}

//1,所有任務(wù)執(zhí)行結(jié)束匯總,不阻塞當(dāng)前線程group.notify(queue: .global(), execute: {

? ? print("group done")

})

//2,永久等待远舅,直到所有任務(wù)執(zhí)行結(jié)束闰蛔,中途不能取消,阻塞當(dāng)前線程group.wait()

print("任務(wù)全部執(zhí)行完成")

9表谊,concurrentPerform 指定次數(shù)的Block最加到隊(duì)列中

DispatchQueue.concurrentPerform函數(shù)是sync函數(shù)和Dispatch Group的關(guān)聯(lián)API钞护。按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中盖喷,并等待全部處理執(zhí)行結(jié)束爆办。

因?yàn)閏oncurrentPerform函數(shù)也與sync函數(shù)一樣,會(huì)等待處理結(jié)束课梳,因此推薦在async函數(shù)中異步執(zhí)行concurrentPerform函數(shù)距辆。concurrentPerform函數(shù)可以實(shí)現(xiàn)高性能的循環(huán)迭代。

//獲取系統(tǒng)存在的全局隊(duì)列l(wèi)et queue = DispatchQueue.global(qos: .default)

//定義一個(gè)異步步代碼塊queue.async {

? ? //通過(guò)concurrentPerform暮刃,循環(huán)變量數(shù)組DispatchQueue.concurrentPerform(iterations:6) {(index) -> Voidin? ? ? ? print(index)

? ? }


? ? //執(zhí)行完畢跨算,主線程更新? ? DispatchQueue.main.async {

? ? ? ? print("done")

? ? }

}

10,信號(hào)椭懊,信號(hào)量

DispatchSemaphore(value: ):用于創(chuàng)建信號(hào)量诸蚕,可以指定初始化信號(hào)量計(jì)數(shù)值,這里我們默認(rèn)1.

semaphore.wait():會(huì)判斷信號(hào)量氧猬,如果為1背犯,則往下執(zhí)行。如果是0盅抚,則等待漠魏。

semaphore.signal():代表運(yùn)行結(jié)束,信號(hào)量加1妄均,有等待的任務(wù)這個(gè)時(shí)候才會(huì)繼續(xù)執(zhí)行柱锹。

//獲取系統(tǒng)存在的全局隊(duì)列l(wèi)et queue = DispatchQueue.global(qos: .default)

//當(dāng)并行執(zhí)行的任務(wù)更新數(shù)據(jù)時(shí)哪自,會(huì)產(chǎn)生數(shù)據(jù)不一樣的情況foriin1...10 {

? ? queue.async {

? ? ? ? print("\(i)")

? ? }

}

//使用信號(hào)量保證正確性//創(chuàng)建一個(gè)初始計(jì)數(shù)值為1的信號(hào)let semaphore = DispatchSemaphore(value:1)foriin1...10 {

? ? queue.async {

? ? ? ? //永久等待,直到Dispatch Semaphore的計(jì)數(shù)值 >= 1? ? ? ? semaphore.wait()

? ? ? ? print("\(i)")

? ? ? ? //發(fā)信號(hào)禁熏,使原來(lái)的信號(hào)計(jì)數(shù)值+1? ? ? ? semaphore.signal()

? ? }

}


原文出自:www.hangge.com轉(zhuǎn)載請(qǐng)保留原文鏈接:http://www.hangge.com/blog/cache/detail_745.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壤巷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞧毙,更是在濱河造成了極大的恐慌隙笆,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件升筏,死亡現(xiàn)場(chǎng)離奇詭異撑柔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)您访,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門铅忿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人灵汪,你說(shuō)我怎么就攤上這事檀训。” “怎么了享言?”我有些...
    開(kāi)封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵峻凫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我览露,道長(zhǎng)荧琼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任差牛,我火速辦了婚禮命锄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偏化。我一直安慰自己脐恩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布侦讨。 她就那樣靜靜地躺著驶冒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪韵卤。 梳的紋絲不亂的頭發(fā)上骗污,一...
    開(kāi)封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音怜俐,去河邊找鬼身堡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拍鲤,可吹牛的內(nèi)容都是我干的贴谎。 我是一名探鬼主播汞扎,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼擅这!你這毒婦竟也來(lái)了澈魄?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仲翎,失蹤者是張志新(化名)和其女友劉穎痹扇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體溯香,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲫构,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玫坛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结笨。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖湿镀,靈堂內(nèi)的尸體忽然破棺而出炕吸,到底是詐尸還是另有隱情,我是刑警寧澤勉痴,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布赫模,位于F島的核電站,受9級(jí)特大地震影響蒸矛,放射性物質(zhì)發(fā)生泄漏瀑罗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一莉钙、第九天 我趴在偏房一處隱蔽的房頂上張望廓脆。 院中可真熱鬧,春花似錦磁玉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吮铭,卻和暖如春时迫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谓晌。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工掠拳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纸肉。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓溺欧,卻偏偏與公主長(zhǎng)得像喊熟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姐刁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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