Swift GCD

一、DispatchQueue

DispatchQueue 分為串行和并發(fā),它的完整初始化方法為:

init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)

可見吝羞,這些參數(shù)中兰伤,除了label,其它都有默認值(label表示該隊列的標簽钧排,建議傳值為反向域名字符串敦腔,如:com.onevcat.Kingfisher.Animator.preloadQueue)。

當(dāng)除label外的參數(shù)都使用默認值時卖氨,初始化方法返回的便是串行隊列会烙。如果需要返回并發(fā)隊列,參數(shù)attributes傳值為.concurrent即可筒捺。DispatchQueue.Attributes 是一個結(jié)構(gòu)體類型柏腻,該結(jié)構(gòu)體提供了兩個靜態(tài)變量:concurrentinitiallyInactive(注意,沒有代表串行隊列的靜態(tài)變量)系吭。如果attributes參數(shù)傳值為initiallyInactive, 任務(wù)不會自動執(zhí)行五嫂,而是需要開發(fā)者手動調(diào)用activate()觸發(fā)。但是代碼依然是串行進行的,如果想要手動觸發(fā)肯尺、并行執(zhí)行任務(wù),可以指定attributes參數(shù)接受一個數(shù)組: [.concurrent, .initiallyInactive]沃缘。

參數(shù)qos代表隊列執(zhí)行的優(yōu)先級,有六種優(yōu)先級可供選擇:

unspecified
background
default
utility
userInteractive
userInitiated

優(yōu)先級從高到低依次為userInteractive>userInitiated>utility>background, 而default與unspecified介于userInteractive與background之間则吟,具體有系統(tǒng)決定槐臀。

DispatchQueue.AutoreleaseFrequency有三種屬性值.inherit.workItem.never氓仲。
.inherit:不確定水慨,之前默認的行為也是現(xiàn)在的默認值
.workItem:為每個執(zhí)行的任務(wù)創(chuàng)建自動釋放池,項目完成時清理臨時對象
.never:GCD不為您管理自動釋放池

參數(shù)target 用于指定即將創(chuàng)建的隊列與隊列target優(yōu)先級相同。也可通過setTarget(queue: DispatchQueue?)函數(shù)指定與queue相同的優(yōu)先級敬扛。

除了開發(fā)者自己創(chuàng)建隊列晰洒,還可以通過DispatchQueue.main獲取主隊列(主隊列也屬于串行隊列)、DispatchQueue.global(qos: DispatchQoS.QoSClass) 獲取全局并發(fā)隊列啥箭。

創(chuàng)建好了隊列谍珊,通過sync { /*任務(wù)*/ }async { /*任務(wù)*/ } 將任務(wù)追加到隊列中。串行隊列或并發(fā)隊列與sync或async組合總結(jié):

串行隊列 + sync : 隊列中的任務(wù)在當(dāng)前線程中依次執(zhí)行急侥,后面追加的任務(wù)會等到前面追加的任務(wù)執(zhí)行完了才開始執(zhí)行砌滞,不開新線程。當(dāng)前線程取任務(wù)執(zhí)行的隊列不能與該串行隊列相同坏怪,否則會發(fā)生線程死鎖贝润。
串行隊列(非主隊列) + async : 隊列中的任務(wù)在新線程中依次執(zhí)行。
主隊列 + async : 將任務(wù)追加到主隊列陕悬,當(dāng)主隊列中的其他任務(wù)執(zhí)行完之后才會執(zhí)行题暖,并且在在主線程中執(zhí)行按傅。
并發(fā)隊列 + sync : 隊列中的任務(wù)在當(dāng)前線程中依次執(zhí)行捉超。
并發(fā)隊列 + async : 隊列中的任務(wù)在新線程中并發(fā)執(zhí)行胧卤。

不管哪種組合,隊列中的任務(wù)出列的方式都是FIFO拼岳。

有時候希望追加到queue中的任務(wù)暫不執(zhí)行枝誊,等待某一時刻執(zhí)行,這時候可使用隊列的suspend()函數(shù)和resume()函數(shù)惜纸。suspend()函數(shù)使隊列的暫停計數(shù)加1叶撒,resume()函數(shù)使隊列的暫停計數(shù)減一。

需要注意:
1耐版、suspend()和resume()需要成對出現(xiàn)祠够,否則會crash。
2粪牲、suspend()和resume()函數(shù)只對自己創(chuàng)建的隊列有效古瓤,對系統(tǒng)提供的全局隊列無效。
3腺阳、suspend()和 resume()對隊列中的還未執(zhí)行的任務(wù)有效落君,對于正在執(zhí)行的任務(wù)無效。

二亭引、DispatchGroup

在追加到DispatchQueue中的多個處理全部結(jié)束后想執(zhí)行結(jié)束處理绎速,這個時候就可用到DispatchGroup。示例如下:

let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group) {
     print("任務(wù)一")
}
queue.async(group: group) {
      print("任務(wù)二")
}
queue.async(group: group) {
     print("任務(wù)三")
}
group.notify(queue: DispatchQueue.main) {
     print("完成任務(wù)一焙蚓、二纹冤、三")
}
queue.async {
     print("任務(wù)四")
}

運行結(jié)果:


其中,queue既可以是同一個隊列主届,也可以是不同的隊列赵哲,既可以是串行隊列,也可以是并發(fā)隊列君丁。
另外枫夺,也可以使用group的 group.wait(timeout: DispatchTime)group.wait(wallTimeout: DispatchWallTime)函數(shù)。wait 函數(shù)的參數(shù)表示等待的時間绘闷,默認是 DispatchTime.distantFuture橡庞,表示永久等待。wait函數(shù)會阻塞當(dāng)前線程印蔗,即當(dāng)執(zhí)行的時間到了等待的時長扒最,才會執(zhí)行后面的代碼。wait函數(shù)返回值是枚舉類型DispatchTimeoutResult华嘹,DispatchTimeoutResult有success吧趣、timeOut兩個枚舉值,分別表示在等待時長內(nèi),任務(wù)執(zhí)行完成和未完成强挫。
我們還可以通過group的enter()函數(shù)和leave()函數(shù)顯式表明任務(wù)是否執(zhí)行完成岔霸。代碼如下:

let group = DispatchGroup()
let queue = DispatchQueue.global()
group.enter()
queue.async {
     print("任務(wù)一")
     group.leave()
}
group.enter()
queue.async {
     print("任務(wù)二")
     group.leave()
}
group.enter()
queue.async {
    print("任務(wù)三")
    group.leave()
}
group.notify(queue: DispatchQueue.main) {
    print("完成任務(wù)一、二俯渤、三")
}
queue.async {
    print("任務(wù)四")
}

運行結(jié)果:


enter()leave()必須配合使用呆细,有幾次enter就要有幾次leave,否則group會一直存在八匠。當(dāng)所有enter的block都leave后絮爷,會執(zhí)行dispatch_group_notify的block。

三梨树、asyncAfter

該函數(shù)用于延時操作坑夯。代碼如下:

DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now()+3) {
    print("執(zhí)行任務(wù)")
} 

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3) {
    print("執(zhí)行任務(wù)")
}

注意, asyncAfter函數(shù)并不是在指定時間后執(zhí)行處理,而是在指定時間后將任務(wù)追加到隊列中抡四。

asyncAfter函數(shù)的第一個參數(shù)可以是DispatchTime類型的值渊涝,也可以是DispatchWallTime類型的值。

DispatchTime 表示相對時間(相對設(shè)備啟動的時間床嫌,當(dāng)設(shè)備休眠時跨释,計時也會暫停),精度為納秒級厌处。DispatchTime.now() 獲取當(dāng)前相對時間鳖谈,DispatchTime.now()基于當(dāng)前時間三秒后的時間,表達式中的3也可以使用DispatchTimeInterval.seconds(3)替換阔涉,或者用其他的時間單位:毫秒級DispatchTimeInterval.milliseconds(Int) 缆娃、微秒級DispatchTimeInterval.milliseconds(Int)、納秒級DispatchTimeInterval.nanoseconds(Int)瑰排。
DispatchWallTime 表示絕對時間(系統(tǒng)時間贯要,設(shè)備休眠計時不暫停),精度是微秒椭住。DispatchWallTime的用法和DispatchTime差不多崇渗。

四、DispatchWorkItem

DispatchWorkItem可以將任務(wù)封裝成DispatchWorkItem對象京郑。

let workItem = DispatchWorkItem.init {
      print("執(zhí)行任務(wù)")
}

可以調(diào)用workItem的perform()函數(shù)執(zhí)行任務(wù)宅广,也可以將workItem追加到DispatchQueue或DispatchGroup中。以上所有傳block的地方都可換成DispatchWorkItem對象些举。
DispatchQueue還可以使用notify函數(shù)觀察workItem中的任務(wù)執(zhí)行結(jié)束跟狱,以及通過cancel()函數(shù)取消任務(wù)。

另外户魏,workItem也可以像DispatchGroup一樣調(diào)用wait()函數(shù)等待任務(wù)完成驶臊。需要注意的是挪挤,追加workItem的隊列或調(diào)用perform()所在的隊列不能與調(diào)用workItem.wait()的隊列是同一個隊列,否則會出現(xiàn)線程死鎖关翎。

DispatchWorkItem的完整初始化方法:

init(qos: DispatchQoS, flags: DispatchWorkItemFlags, block: () -> Void)

DispatchQoS前面已經(jīng)說過电禀,不再贅述。DispatchWorkItemFlags類型的變量有六種:

static let assignCurrentContext: DispatchWorkItemFlags
static let barrier: DispatchWorkItemFlags
static let detached: DispatchWorkItemFlags
static let enforceQoS: DispatchWorkItemFlags
static let inheritQoS: DispatchWorkItemFlags
static let noQoS: DispatchWorkItemFlags

為了高效地讀寫數(shù)據(jù)庫或文件笤休,通常需要將讀寫處理追加到并發(fā)隊列
中異步執(zhí)行,為了使讀寫操作不會引發(fā)數(shù)據(jù)競爭的問題症副,寫入操作不能與其他的寫入操作以及包含讀取任務(wù)的操作并發(fā)處理店雅,這時便可設(shè)置flag的值為.barrier。代碼如下:

let queue = DispatchQueue.init(label: "com.codansYC.queue", attributes: DispatchQueue.Attributes.concurrent)
queue.async {
     print("讀數(shù)據(jù)1")
}
queue.async {
     print("讀數(shù)據(jù)2")
}
let workItem = DispatchWorkItem.init(qos: DispatchQoS.default, flags: DispatchWorkItemFlags.barrier) {
     print("開始寫數(shù)據(jù)------寫數(shù)據(jù)完成")
}
queue.async(execute: workItem)
queue.async {
     print("讀數(shù)據(jù)3")
}
queue.async {
     print("讀數(shù)據(jù)4")
}

運行結(jié)果:


注意贞铣,barrier只對自己創(chuàng)建的并發(fā)隊列才有效闹啦,對系統(tǒng)提供的全局并發(fā)隊列無效。

五辕坝、DispatchQueue.concurrentPerform

sync函數(shù)和Dispatch Group的關(guān)聯(lián)API窍奋。
DispatchQueue.concurrentPerform 會按指定次數(shù)異步執(zhí)行任務(wù),并且會等待指定次數(shù)的任務(wù)全部執(zhí)行完成酱畅,即會阻塞線程琳袄。建議在子線程中使用。

DispatchQueue.global().async {
     DispatchQueue.concurrentPerform(iterations: 5) { (i) in
         print("執(zhí)行任務(wù)\(i+1)")
     }
     print("任務(wù)執(zhí)行完成")
}

運行結(jié)果:

六纺酸、DispatchSemaphore

信號量窖逗。用于控制訪問資源的數(shù)量。比如系統(tǒng)有兩個資源可以被利用餐蔬,同時有三個線程要訪問碎紊,只能允許兩個線程訪問,第三個會等待資源被釋放后再訪問樊诺。
信號量的初始化方法:DispatchSemaphore.init(value: Int)仗考,value表示允許訪問資源的線程數(shù)量,當(dāng)value為0時對訪問資源的線程沒有限制词爬。
信號量配套使用wait()函數(shù)與signal()函數(shù)控制訪問資源秃嗜。
wait函數(shù)會阻塞當(dāng)前線程直到信號量計數(shù)大于或等于1,當(dāng)信號量大于或等于1時顿膨,將信號量計數(shù)-1, 然后執(zhí)行后面的代碼痪寻。signal()函數(shù)會將信號量計數(shù)+1。

信號量是GCD同步的一種方式虽惭。前面介紹過的DispatchWorkItemFlags.barrier是對queue中的任務(wù)進行批量同步處理橡类,sync函數(shù)是對queue中的任務(wù)單個同步處理,而DispatchSemaphore是對queue中的某個任務(wù)中的某部分(某段代碼)同步處理芽唇。此時將DispatchSemaphore.init(value: Int)中的參數(shù)value傳入1顾画。代碼如下:

var arr = [Int]()
let semaphore = DispatchSemaphore.init(value: 1) // 創(chuàng)建信號量取劫,控制同時訪問資源的線程數(shù)為1
for i in 0...100 {
    DispatchQueue.global().async {
                
        /*
        其他并發(fā)操作
        */
                
        semaphore.wait() // 如果信號量計數(shù)>=1,將信號量計數(shù)減1;如果信號量計數(shù)<1研侣,阻塞線程直到信號量計數(shù)>=1
        arr.append(i)
        semaphore.signal() // 信號量計加1
                
        /*
        其他并發(fā)操作
        */
     }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谱邪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子庶诡,更是在濱河造成了極大的恐慌惦银,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件末誓,死亡現(xiàn)場離奇詭異扯俱,居然都是意外死亡,警方通過查閱死者的電腦和手機喇澡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門迅栅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晴玖,你說我怎么就攤上這事读存。” “怎么了呕屎?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵让簿,是天一觀的道長。 經(jīng)常有香客問我秀睛,道長拜英,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任琅催,我火速辦了婚禮居凶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘藤抡。我一直安慰自己侠碧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布缠黍。 她就那樣靜靜地躺著弄兜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瓷式。 梳的紋絲不亂的頭發(fā)上替饿,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音贸典,去河邊找鬼视卢。 笑死,一個胖子當(dāng)著我的面吹牛廊驼,可吹牛的內(nèi)容都是我干的据过。 我是一名探鬼主播惋砂,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绳锅!你這毒婦竟也來了西饵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鳞芙,失蹤者是張志新(化名)和其女友劉穎眷柔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體原朝,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡驯嘱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了竿拆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡宾尚,死狀恐怖丙笋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情煌贴,我是刑警寧澤御板,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牛郑,受9級特大地震影響怠肋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜淹朋,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一笙各、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧础芍,春花似錦杈抢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滑凉。三九已至虐呻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欠气,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留豹储,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓淘这,卻偏偏與公主長得像颂翼,于是被迫代替她去往敵國和親晃洒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

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