GCD 細(xì)細(xì)的讀

目錄

<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è)而完全阻塞。

如下圖所示

gcd-deadlock-1.png
  • 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>的完全阻塞冈爹,程序停止;

如下圖所示

線程死鎖2.png
  • 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剥汤。

如下圖所示

線程死鎖3.png
  • 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ì)列的同步操作!

如下圖所示

線程死鎖4.png

二唾那、并行隊(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鹊汛。

如下圖所示

線程死鎖5.png
  • 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)繞,要消化一下厕怜!

如下圖所示

線程死鎖6.png

<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í)
    1. userInteractive
    2. userInitiated
    3. default
    4. utility
    5. background
    6. unspecified
  • attributes
    1. concurrent: 并行自動(dòng)模式
    2. initiallyInactive: 還是串行粥航,但需要手動(dòng)啟動(dòng) (如queue.activate())
  • autoreleaseFrequency
    1. inherit 繼承
    2. workItem 工作項(xiàng)目
    3. 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多線程,你看我就夠了哮内、

作者:燈泡蟲

郵箱:developer_yh@163.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盗棵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子北发,更是在濱河造成了極大的恐慌纹因,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琳拨,死亡現(xiàn)場(chǎng)離奇詭異瞭恰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狱庇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門惊畏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)是牢,“玉大人,你說(shuō)我怎么就攤上這事陕截。” “怎么了批什?”我有些...
    開(kāi)封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵农曲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我驻债,道長(zhǎng)乳规,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任合呐,我火速辦了婚禮暮的,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淌实。我一直安慰自己冻辩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拆祈。 她就那樣靜靜地躺著恨闪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪放坏。 梳的紋絲不亂的頭發(fā)上咙咽,一...
    開(kāi)封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音淤年,去河邊找鬼钧敞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛麸粮,可吹牛的內(nèi)容都是我干的溉苛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼豹休,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炊昆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起威根,我...
    開(kāi)封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凤巨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后洛搀,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體敢茁,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年留美,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彰檬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伸刃。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖逢倍,靈堂內(nèi)的尸體忽然破棺而出捧颅,到底是詐尸還是另有隱情,我是刑警寧澤较雕,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布碉哑,位于F島的核電站,受9級(jí)特大地震影響亮蒋,放射性物質(zhì)發(fā)生泄漏扣典。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一慎玖、第九天 我趴在偏房一處隱蔽的房頂上張望贮尖。 院中可真熱鬧,春花似錦趁怔、人聲如沸湿硝。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)图柏。三九已至,卻和暖如春任连,著一層夾襖步出監(jiān)牢的瞬間蚤吹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工随抠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裁着,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓拱她,卻偏偏與公主長(zhǎng)得像二驰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秉沼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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