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