目錄
- 前言
- 為什么選擇GCD羹唠?
- 串行隊(duì)列、并行隊(duì)列际起、同步悍缠、異步
- 線程死鎖解析
- DispatchQueue的使用
- Dispatchgroup的使用
- DispatchSourceTiemr的使用
- 延遲執(zhí)行
<a name="前言"></a>前言
在日常app開(kāi)發(fā)中多線程的使用是難免的,既然躲不過(guò)姑荷,干嘛不好好的享受盒延? 本文將對(duì)GCD進(jìn)行全面的解析缩擂。
<a name="為什么選擇GCD?"></a>為什么選擇GCD添寺?
它是蘋果在 iOS 4 時(shí)推出的,為多核的并行運(yùn)算提出的, 以c語(yǔ)言編寫的解決方案胯盯。高效,自動(dòng)管理是它的優(yōu)點(diǎn)。
<a name="串行隊(duì)列计露、并行隊(duì)列博脑、同步、異步"></a>串行隊(duì)列薄坏、并行隊(duì)列趋厉、同步、異步
串行隊(duì)列:放到串行隊(duì)列的任務(wù)胶坠,GCD 會(huì)FIFO(先進(jìn)先出)地取出來(lái)一個(gè)君账,執(zhí)行一個(gè),然后取下一個(gè)沈善,這樣一個(gè)一個(gè)的執(zhí)行乡数。
并行隊(duì)列
- 為異步執(zhí)行時(shí):放到并行隊(duì)列的任務(wù),GCD 也會(huì)FIFO的取出來(lái)闻牡,但不同的是净赴,它取出來(lái)一個(gè)就會(huì)放到別的線程,然后再取出來(lái)一個(gè)又放到另一個(gè)的線程罩润。這樣由于取的動(dòng)作很快玖翅,忽略不計(jì),看起來(lái)割以,所有的任務(wù)都是一起執(zhí)行的金度。不過(guò)需要注意,GCD 會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量严沥,所以如果任務(wù)很多猜极,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行。
- 為同步時(shí):和串行隊(duì)列基本一致消玄。
同步:會(huì)先阻塞當(dāng)前線程跟伏,直到任務(wù)完成。
異步:會(huì)另外開(kāi)線程處理翩瓜,不會(huì)阻塞當(dāng)前線程受扳。
串行情況下的 同步和異步
func testFive() {
//串行隊(duì)列
let qe = DispatchQueue.init(label: "concurrentQueue")
print("當(dāng)前線程",Thread.current)
//同步
qe.sync {
for i in 0..<5 {
print("?? - \(i)",Thread.current)
}
}
qe.sync {
for i in 0..<5 {
print("?? - \(i)",Thread.current)
}
}
//異步
qe.async {
for i in 0..<5 {
print("???? - \(i)",Thread.current)
}
}
qe.async {
for i in 0..<5 {
print("???? - \(i)",Thread.current)
}
}
}
當(dāng)前線程 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 0 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 1 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 2 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 3 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 4 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 0 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 1 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 2 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 3 <NSThread: 0x60800006d540>{number = 1, name = main}
?? - 4 <NSThread: 0x60800006d540>{number = 1, name = main}
???? - 0 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 1 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 2 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 3 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 4 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 0 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 1 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 2 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 3 <NSThread: 0x600000078f40>{number = 3, name = (null)}
???? - 4 <NSThread: 0x600000078f40>{number = 3, name = (null)}
結(jié)論:可以看出,在串行情況下奥溺,不管是異步還是同步都是一個(gè)個(gè)按照順序執(zhí)行辞色。唯一的區(qū)別就是異步單獨(dú)開(kāi)了個(gè)線程
并行情況下的 同步和異步
func testFive() {
//其實(shí)就是初始化函數(shù)里,多了個(gè)concurrent 的參數(shù)
let qe = DispatchQueue.init(label: "concurrentQueue", attributes:.concurrent)
print("當(dāng)前線程",Thread.current)
//同步
qe.sync {
for i in 0..<5 {
print("?? - \(i)",Thread.current)
}
}
qe.sync {
for i in 0..<5 {
print("?? - \(i)",Thread.current)
}
}
//異步
qe.async {
for i in 0..<5 {
print("???? - \(i)",Thread.current)
}
}
qe.async {
for i in 0..<5 {
print("???? - \(i)",Thread.current)
}
}
}
當(dāng)前線程 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 0 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 1 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 2 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 3 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 4 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 0 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 1 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 2 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 3 <NSThread: 0x60000006b380>{number = 1, name = main}
?? - 4 <NSThread: 0x60000006b380>{number = 1, name = main}
???? - 0 <NSThread: 0x608000078640>{number = 3, name = (null)}
???? - 0 <NSThread: 0x6000000767c0>{number = 4, name = (null)}
???? - 1 <NSThread: 0x608000078640>{number = 3, name = (null)}
???? - 1 <NSThread: 0x6000000767c0>{number = 4, name = (null)}
???? - 2 <NSThread: 0x608000078640>{number = 3, name = (null)}
???? - 2 <NSThread: 0x6000000767c0>{number = 4, name = (null)}
???? - 3 <NSThread: 0x608000078640>{number = 3, name = (null)}
???? - 3 <NSThread: 0x6000000767c0>{number = 4, name = (null)}
???? - 4 <NSThread: 0x608000078640>{number = 3, name = (null)}
???? - 4 <NSThread: 0x6000000767c0>{number = 4, name = (null)}
結(jié)論:可以看出,在并行情況下相满,同步時(shí)和串行一致层亿。異步則會(huì)開(kāi)多個(gè)線程并行執(zhí)行。
當(dāng)然立美,如果你還是有點(diǎn)暈的話可以看下面這個(gè)表格匿又,一般情況都可以用下面的表格解釋(例外看下面的注意點(diǎn)!)
同步執(zhí)行 | 異步執(zhí)行 | |
---|---|---|
串行隊(duì)列 | 當(dāng)前線程建蹄,一個(gè)一個(gè)執(zhí)行 | 其他線程碌更,一個(gè)一個(gè)執(zhí)行 |
并行隊(duì)列 | 當(dāng)前線程,一個(gè)一個(gè)執(zhí)行 | 開(kāi)很多線程洞慎,一起執(zhí)行 |
注意點(diǎn):經(jīng)過(guò)測(cè)試痛单,得到的經(jīng)驗(yàn):(1)主線程中的其他隊(duì)列異步都能分出線程(系統(tǒng)線程資源沒(méi)用完前),(2)分線程中的其他隊(duì)列異步不一定分出線程劲腿;(3)主線程中的主隊(duì)列異步不能分出線程旭绒。
<a name="線程死鎖解析"></a>線程死鎖解析
一、串行隊(duì)列
- 1焦人、主線程死鎖
func syncFirst() {
print("MainQueue-Task1",Thread.current)
DispatchQueue.main.sync {
print("MainQueue-Task2-sync",Thread.current)
}
print("MainQueue-Task3",Thread.current)
}
輸出:1
MainQueue-Task1 <NSThread: 0x60000006e640>{number = 1, name = main}
解析:
在主隊(duì)列存在3個(gè)任務(wù)(任務(wù)1挥吵,同步任務(wù)2,任務(wù)3)花椭,按照正常的隊(duì)列的順序執(zhí)行順序應(yīng)該為
任務(wù)1-->同步任務(wù)2-->任務(wù)3忽匈;但是因?yàn)橥饺蝿?wù)2的存在,導(dǎo)致主線程阻塞矿辽,且同步任務(wù)2被抽出重新入隊(duì)列丹允,于是同步任務(wù)2,在主隊(duì)列中排在了任務(wù)3的后面袋倔。那么問(wèn)題來(lái)了嫌松,現(xiàn)在有兩條線 1: 任務(wù)1-->同步任務(wù)2-->任務(wù)3 // 2: 任務(wù)1-->任務(wù)3-->同步任務(wù)2 于是主線程就會(huì)因?yàn)榛ハ嗟却恢老葓?zhí)行哪個(gè)而完全阻塞。
如下圖所示
- 2奕污、其他隊(duì)列的阻塞
func syncSec() {
let queue = DispatchQueue.init(label: "MyQueue")
print("MainQueue-Task1",Thread.current)
queue.async {
print("MyQueue-Task2",Thread.current)
queue.sync(execute: {
print("MyQueue-Task3",Thread.current)
})
print("MyQueue-Task4",Thread.current)
}
print("MainQueue-Task5",Thread.current)
}
輸出:1 -(2/5)
MainQueue-Task1 <NSThread: 0x608000071400>{number = 1, name = main}
MainQueue-Task5 <NSThread: 0x608000071400>{number = 1, name = main}
MyQueue-Task2 <NSThread: 0x60000007d200>{number = 3, name = (null)}
解析:
在主隊(duì)列存在3個(gè)任務(wù)(任務(wù)1,異步任務(wù)液走,任務(wù)5)碳默,那么毫無(wú)疑問(wèn)任務(wù)1最先執(zhí)行,因中間隔著是異步線程缘眶,故任務(wù)5可以和異步線程同時(shí)開(kāi)始執(zhí)行嘱根;MyQueue串行隊(duì)列中存在3個(gè)任務(wù)(同步任務(wù)2,同步任務(wù),同步任務(wù))巷懈,任務(wù)2先執(zhí)行,然后遇到了同步操作,那么還是和上個(gè)問(wèn)題一樣株憾,同步任務(wù)3和任務(wù)4之間相互等待,造成 線程<NSThread: 0x60000007d200>的完全阻塞冈爹,程序停止;
如下圖所示
- 3欧引、錯(cuò)覺(jué):只要串行隊(duì)列中有同步操作频伤,就會(huì)立馬死鎖?
看完第一個(gè)和第二個(gè)的例子是不是有一種錯(cuò)覺(jué):只要串行隊(duì)列中有同步操作芝此,就會(huì)立馬死鎖憋肖?其實(shí)不然,下面的例子就證明了有串行同步操作也是可以的婚苹。
func syncThree() {
let qe = DispatchQueue.init(label: "MyQueue")
print("MainQueue-Task1",Thread.current)
qe.sync {
print("MyQueue-Task2",Thread.current)
print("MyQueue-Task3",Thread.current)
}
print("MainQueue-Task4",Thread.current)
}
輸出:1-2-3 岸更,且沒(méi)有推出程序
MainQueue-Task1 <NSThread: 0x600000079000>{number = 1, name = main}
MyQueue-Task2 <NSThread: 0x600000079000>{number = 1, name = main}
MyQueue-Task3 <NSThread: 0x600000079000>{number = 1, name = main}
MainQueue-Task4 <NSThread: 0x600000079000>{number = 1, name = main}
解析:
主隊(duì)列中3個(gè)任務(wù)(任務(wù)1,同步任務(wù)膊升,任務(wù)4)怎炊,MyQueue隊(duì)列中兩個(gè)任務(wù)(任務(wù)2,任務(wù)3)用僧。主線程首選執(zhí)行主隊(duì)列中的任務(wù)1结胀,然后遇到了同步任務(wù),關(guān)鍵來(lái)了责循!因同步任務(wù)是MyQueue的全部任務(wù)集合糟港,故不會(huì)和主隊(duì)列進(jìn)行沖突,而是按照官方描述的院仿,遇到同步任務(wù)要執(zhí)行完任務(wù)中的事情秸抚,于是任務(wù)2和任務(wù)3相繼被執(zhí)行,之后主隊(duì)列的任務(wù)4出隊(duì)列被執(zhí)行歹垫! 順序?yàn)椋?->2->3->4剥汤。
如下圖所示
- 4、串行隊(duì)列下到底怎樣的情況下才會(huì)造成死鎖排惨?
func syncFour() {
let qe = DispatchQueue.init(label: "MyQueue")//, attributes:.concurrent)
print("MainQueue-Task1",Thread.current)
qe.sync {
print("MyQueue-Task2",Thread.current)
qe.sync {
print("MyQueue-Task3",Thread.current)
}
print("MyQueue-Task4",Thread.current)
}
print("MainQueue-Task5",Thread.current)
}
輸出 1-2
MainQueue-Task1 <NSThread: 0x60800006f240>{number = 1, name = main}
MyQueue-Task2 <NSThread: 0x60800006f240>{number = 1, name = main}
解析:
主隊(duì)列中3個(gè)任務(wù)(任務(wù)1吭敢,同步任務(wù),任務(wù)5)暮芭,MyQueue隊(duì)列中三個(gè)任務(wù)(任務(wù)2鹿驼,同步任務(wù)3,任務(wù)4)辕宏。
主線程首先執(zhí)行任務(wù)1畜晰,之后遇到MyQueue的同步任務(wù),跳到MyQueue中執(zhí)行任務(wù)2瑞筐,又遇到了MyQueue的同步任務(wù)凄鼻,那么同步任務(wù)中的任務(wù)3被推入隊(duì)列中,導(dǎo)致任務(wù)3跑到任務(wù)4的后面,導(dǎo)致了任務(wù)3和任務(wù)4的相互等待块蚌,造成死鎖闰非!
我們回到第一個(gè)例子的代碼中再思考一下,和這段代碼有沒(méi)有共同點(diǎn)匈子?河胎?其實(shí)我們可以理解為下面這段偽代碼
//偽代碼,為了說(shuō)明
MainQueue.sync{
viewDidload{
....
syncFirst();
....
}
}
func syncFirst() {
print("MainQueue-Task1",Thread.current)
DispatchQueue.main.sync {
print("MainQueue-Task2-sync",Thread.current)
}
print("MainQueue-Task3",Thread.current)
}
是不是明白了什么虎敦?整個(gè)主線程已經(jīng)是在串行同步的條件下了游岳,
所以我么可以總結(jié)一下串行隊(duì)列的死鎖情況:串行隊(duì)列中有屬于自身隊(duì)列的同步操作,就會(huì)立馬死鎖其徙!或者說(shuō) 任何一個(gè)串行隊(duì)列胚迫,不能添加兩個(gè)本隊(duì)列的同步操作!
如下圖所示
二唾那、并行隊(duì)列
- 1访锻、并行隊(duì)列下的同步
func syncFive() {
let qe = DispatchQueue.init(label: "MyQueue", attributes:.concurrent)
print("MainQueue-Task1",Thread.current)
qe.sync {
print("MyQueue-Task2",Thread.current)
qe.sync {
print("MyQueue-Task3",Thread.current)
}
print("MyQueue-Task4",Thread.current)
}
print("MainQueue-Task5",Thread.current)
}
輸出:1-2-3-4-5
MainQueue-Task1 <NSThread: 0x608000076480>{number = 1, name = main}
MyQueue-Task2 <NSThread: 0x608000076480>{number = 1, name = main}
MyQueue-Task3 <NSThread: 0x608000076480>{number = 1, name = main}
MyQueue-Task4 <NSThread: 0x608000076480>{number = 1, name = main}
MainQueue-Task5 <NSThread: 0x608000076480>{number = 1, name = main}
解析
主隊(duì)列中3個(gè)任務(wù)(任務(wù)1,同步任務(wù)闹获,任務(wù)5)期犬,MyQueue隊(duì)列中三個(gè)任務(wù)(任務(wù)2,同步任務(wù)3避诽,任務(wù)4)龟虎。
主線程首先執(zhí)行任務(wù)1,遇到MyQueue的同步任務(wù)跳到MyQueue隊(duì)列中沙庐,執(zhí)行任務(wù)2鲤妥,此時(shí)遇到了MyQueue自己隊(duì)列的同步任務(wù)3,因是并行情況所有所以直接執(zhí)行(ps:至于為什么直接過(guò)去了拱雏,我還沒(méi)找到原理般的解釋棉安,先相信知乎上這篇文章上第一個(gè)回答所說(shuō)的,如果大家知道原理铸抑,可以告訴我贡耽。)
執(zhí)行完成后再回到主隊(duì)列,執(zhí)行任務(wù)5鹊汛。
如下圖所示
- 2菇爪、 并行下也是有可能出現(xiàn)死鎖的,別大意
func testThree() {
// 全局線程
let queueGlobal = DispatchQueue.global()
// 主線程
let queueMain = DispatchQueue.main
print("MainQueue-Task1",Thread.current)
//全局異步
queueGlobal.async {
print("GlobalQueue-Task2 ",Thread.current)
//主線程同步柒昏,因主線程阻塞,block內(nèi)容和while循環(huán)相互等待
queueMain.sync(execute: {
print("GlobalQueue-Task3 ",Thread.current)
})
print("GlobalQueue-Task4",Thread.current)
}
print("MainQueue-Task5",Thread.current)
sleep(3);
print("MainQueue-Task6")
}
輸出 1-(5/2)-6-3-4
MainQueue-Task1 <NSThread: 0x60000007d4c0>{number = 1, name = main}
MainQueue-Task5 <NSThread: 0x60000007d4c0>{number = 1, name = main}
GlobalQueue-Task2 <NSThread: 0x608000269680>{number = 3, name = (null)}
MainQueue-Task6
GlobalQueue-Task3 <NSThread: 0x60000007d4c0>{number = 1, name = main}
GlobalQueue-Task4 <NSThread: 0x608000269680>{number = 3, name = (null)}
解析
看圖就知道這個(gè)比較麻煩熙揍!主隊(duì)列中6個(gè)任務(wù)(任務(wù)1职祷,異步任務(wù),任務(wù)5,休眠任務(wù)有梆,任務(wù)6,和后面入對(duì)的任務(wù)3)是尖,GlobleQueue隊(duì)列中三個(gè)任務(wù)(任務(wù)2,同步任務(wù)泥耀,任務(wù)4)饺汹。主線程執(zhí)行任務(wù)1,遇到異步則分出線程執(zhí)行任務(wù)2痰催,同時(shí)主線程繼續(xù)執(zhí)行任務(wù)5兜辞,所以任務(wù)2和5位置不一定。現(xiàn)在有兩條線程夸溶,主線程上進(jìn)入了3秒休眠逸吵。分線程上因遇到主線程的同步任務(wù),主線程為串行同步隊(duì)列缝裁,故需要把任務(wù)3加入到主線程的隊(duì)尾扫皱,在任務(wù)6之后。休眠結(jié)束后捷绑,主線程繼續(xù)任務(wù)6和任務(wù)3韩脑,任務(wù)3執(zhí)行完之后,同步任務(wù)才得以完成粹污,所以任務(wù)4才能執(zhí)行段多。有點(diǎn)繞,要消化一下厕怜!
如下圖所示
<a name="DispatchQueue的使用"></a>DispatchQueue的使用
初始化
let queue = DispatchQueue.init(label:String,qos:DispatchQoS,attributes:DispatchQueue.Attributes,autoreleaseFrequency:DispatchQueue.AutoreleaseFrequency, target: DispatchQueue?)
屬性
- label 隊(duì)列的標(biāo)識(shí) (required字段,下面都是option字段)
- qos (quality of server) 服務(wù)質(zhì)量衩匣,也可以說(shuō)是優(yōu)先級(jí),優(yōu)先級(jí)高的得到的資源和權(quán)限就越高!下面為從高到低的優(yōu)先級(jí)
- userInteractive
- userInitiated
- default
- utility
- background
- unspecified
- attributes
- concurrent: 并行自動(dòng)模式
- initiallyInactive: 還是串行粥航,但需要手動(dòng)啟動(dòng) (如queue.activate())
- autoreleaseFrequency
- inherit 繼承
- workItem 工作項(xiàng)目
- never
- target 新的一個(gè)queue琅捏。注意:如果傳這個(gè)參數(shù)的話,前面的配置無(wú)效递雀,全部按照新傳入的queue本身的配置柄延。(親測(cè))
<a name="Dispatchgroup的使用"></a>Dispatchgroup的使用
初始化
let group = DispatchGroup.init()
實(shí)戰(zhàn)
下面的一段代碼很好的解決了多個(gè)網(wǎng)絡(luò)請(qǐng)求并行的問(wèn)題。
func testGroup() {
let group = DispatchGroup.init()
let queueOne = DispatchQueue.init(label: "queueOne",attributes:.concurrent)
let firstTime = NSDate.init().timeIntervalSince1970
print("主線程",Thread.current)
print("開(kāi)始時(shí)間",firstTime)
//這里和下面的三段模擬日常的網(wǎng)絡(luò)請(qǐng)求
group.enter()
queueOne.async(group: group) {
sleep(1) //模擬請(qǐng)求延遲
print("1結(jié)束")
group.leave()
}
group.enter()
queueOne.async(group: group) {
sleep(2)
print("2結(jié)束")
group.leave()
}
group.enter()
queueOne.async(group: group) {
sleep(3)
print("3結(jié)束")
group.leave()
}
group.notify(queue: DispatchQueue.main) {
let secTime = NSDate.init().timeIntervalSince1970
print("所有異步執(zhí)行結(jié)束時(shí)間",secTime)
print("主線程",Thread.current)
}
}
輸出:
主線程 <NSThread: 0x60000006e800>{number = 1, name = main}
開(kāi)始時(shí)間 1487831647.5901
1結(jié)束
2結(jié)束
3結(jié)束
所有異步執(zhí)行結(jié)束時(shí)間 1487831650.66429
主線程 <NSThread: 0x60000006e800>{number = 1, name = main}
解析
首先先創(chuàng)建一個(gè)group和一個(gè)并行的queue;然后往group里添加三段 并行queue異步的模擬網(wǎng)絡(luò)請(qǐng)求缀程,分別延遲1s,2s,3s搜吧。看輸出你就可以看出杨凑,整個(gè)三段請(qǐng)求的時(shí)間是按照 請(qǐng)求時(shí)間最大的一段來(lái)決定的滤奈,所以是3s的時(shí)間。等所有的請(qǐng)求都完成之后撩满,就會(huì)執(zhí)行g(shù)roup的notify回調(diào)蜒程,傳人主線程绅你,就可以刷新UI拉。
注意點(diǎn)
enter 和 leave實(shí)際執(zhí)行的次數(shù)得是1:1的昭躺,不然就會(huì)crash忌锯。所以我們平時(shí)就可以在網(wǎng)絡(luò)請(qǐng)求的成功和失敗block各放一個(gè)leave。因其要么失敗要么成功领炫,比例還是1:1偶垮。
<a name="DispatchSourceTiemr的使用"></a>DispatchSourceTiemr的使用
初始化
(評(píng)論中有朋友遇到過(guò) 定時(shí)器不執(zhí)行的問(wèn)題,是因?yàn)?沒(méi)把 定時(shí)器全局化 帝洪。如:)
var timer:DispatchSourceTimer? 感謝XIAODAO同學(xué) 的建議似舵,哈哈)
let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags.strict, queue: DispatchQueue.main)
當(dāng)然也可以簡(jiǎn)單的初始化:
let timer = DispatchSource.makeTimerSource()//默認(rèn)了主線程
注意點(diǎn)
timer要全局定義,上面那樣局部定義初始化是不會(huì)執(zhí)行的
屬性
- flags 一般不用傳
- queue 定時(shí)器運(yùn)行的隊(duì)列碟狞,傳入主隊(duì)列 在主線程執(zhí)行啄枕;不傳則默認(rèn)在分線程
主要方法
scheduleOneshot 循環(huán)單次
-
scheduleRepeating 重復(fù)循環(huán)
兩個(gè)方法的屬性
- wallDeadline 延遲執(zhí)行
- leeway 允許的誤差
- deadline 執(zhí)行的時(shí)間
- interval 循環(huán)模式時(shí)的間隔時(shí)間
來(lái)看幾段代碼
單次執(zhí)行
func runOnce() {
print("主線程",Thread.current)
//創(chuàng)建主線程下的定時(shí)器
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
//單次執(zhí)行
timer?.scheduleOneshot(deadline: DispatchTime.now(), leeway: .milliseconds(10))
//執(zhí)行的代碼塊
timer?.setEventHandler {
print("單次執(zhí)行",Thread.current)
}
//繼續(xù),用activate()也行
timer?.resume()
}
輸出:
主線程 <NSThread: 0x6080000740c0>{number = 1, name = main}
單次執(zhí)行 <NSThread: 0x6080000740c0>{number = 1, name = main}
多次執(zhí)行
func runMul() {
print("主線程",Thread.current)
創(chuàng)建分線程下的定時(shí)器
timer = DispatchSource.makeTimerSource()
//循環(huán)執(zhí)行族沃,馬上開(kāi)始频祝,間隔為1s,誤差允許10微秒
timer?.scheduleRepeating(deadline: DispatchTime.now(), interval: .seconds(1), leeway: .milliseconds(10))
//執(zhí)行內(nèi)容
timer?.setEventHandler {
print("1 second interval",Thread.current)
}
//激活
timer?.activate()
}
輸出:
主線程 <NSThread: 0x60000006f0c0>{number = 1, name = main}
1 second interval <NSThread: 0x60000006f0c0>{number = 1, name = main}
1 second interval <NSThread: 0x60000006f0c0>{number = 1, name = main}
1 second interval <NSThread: 0x60000006f0c0>{number = 1, name = main}
...
...
取消執(zhí)行
func runCancel() {
print("主線程",Thread.current)
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
timer?.scheduleRepeating(deadline: DispatchTime.now(), interval: .seconds(1), leeway: .milliseconds(10))
timer?.setEventHandler {
print("1 second interval",Thread.current)
sleep(2)
print("after sleep")
}
timer?.activate()
//主隊(duì)列創(chuàng)建的情況下 如果在這里直接cancel(),handler里面的內(nèi)容是不能執(zhí)行的脆淹。如果是默認(rèn)的分線程常空,則是可以的,至于為什么盖溺,我前面多線程的時(shí)候解釋過(guò)了哈漓糙。
//timer?.cancel()
//最好都這樣取消
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1, execute: {
self.timer?.cancel()
})
}
輸出:
主線程 <NSThread: 0x6080000644c0>{number = 1, name = main}
1 second interval <NSThread: 0x60000006f000>{number = 3, name = (null)}
after sleep
<a name="延遲執(zhí)行"></a>延遲執(zhí)行
GCD的延遲執(zhí)行一般來(lái)說(shuō)可以分為兩大類
- 1、 隊(duì)列直接asyncAfter
- 2烘嘱、 定時(shí)器
如下代碼所示
func testDelay() {
let queue = DispatchQueue.init(label: "myqueue")
let delayTime = DispatchTime.now() + 2.0
let delayTimeTimer = DispatchWallTime.now() + 2.0
print("before delay")
//第一種
queue.asyncAfter(deadline: delayTime) {
print("這是 asyncAfter delay")
}
//第二種
let workItem = DispatchWorkItem.init {
print("這是 asyncAfter workItem delay")
}
queue.asyncAfter(deadline: delayTime, execute: workItem)
//第三種
timer = DispatchSource.makeTimerSource()
timer?.scheduleOneshot(wallDeadline:delayTimeTimer)
timer?.setEventHandler(handler: {
print("這是 timer delay")
})
timer?.activate()
}
輸出:
before delay
這是 timer delay
這是 asyncAfter delay
這是 asyncAfter workItem delay
總結(jié)
針對(duì)本文的觀點(diǎn)昆禽,如有錯(cuò)誤點(diǎn),煩請(qǐng)指出蝇庭!
本文引用以下文章的部分觀點(diǎn):五個(gè)案例讓你明白GCD死鎖醉鳖、關(guān)于iOS多線程,你看我就夠了哮内、
作者:燈泡蟲