GCD 細細的讀

目錄

<a name="前言"></a>前言

在日常app開發(fā)中多線程的使用是難免的,既然躲不過,干嘛不好好的享受? 本文將對GCD進行全面的解析。

<a name="為什么選擇GCD?"></a>為什么選擇GCD?

它是蘋果在 iOS 4 時推出的,為多核的并行運算提出的, 以c語言編寫的解決方案。高效,自動管理是它的優(yōu)點。

<a name="串行隊列、并行隊列、同步、異步"></a>串行隊列、并行隊列、同步、異步

串行隊列:放到串行隊列的任務(wù),GCD 會FIFO(先進先出)地取出來一個,執(zhí)行一個,然后取下一個,這樣一個一個的執(zhí)行。

并行隊列

  • 為異步執(zhí)行時:放到并行隊列的任務(wù),GCD 也會FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。這樣由于取的動作很快,忽略不計,看起來,所有的任務(wù)都是一起執(zhí)行的。不過需要注意,GCD 會根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會讓所有任務(wù)同時執(zhí)行。
  • 為同步時:和串行隊列基本一致。

同步:會先阻塞當前線程,直到任務(wù)完成。

異步:會另外開線程處理,不會阻塞當前線程。

串行情況下的 同步和異步

  func testFive() {
        //串行隊列
        let qe =  DispatchQueue.init(label: "concurrentQueue")
       
        print("當前線程",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)
            }
        }
    }

當前線程 <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é)論:可以看出,在串行情況下,不管是異步還是同步都是一個個按照順序執(zhí)行。唯一的區(qū)別就是異步單獨開了個線程

并行情況下的 同步和異步

  func testFive() {
        //其實就是初始化函數(shù)里,多了個concurrent 的參數(shù)
        let qe =  DispatchQueue.init(label: "concurrentQueue", attributes:.concurrent)
       
        print("當前線程",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)
            }
        }
    }
    
    當前線程 <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é)論:可以看出,在并行情況下,同步時和串行一致。異步則會開多個線程并行執(zhí)行。

當然,如果你還是有點暈的話可以看下面這個表格,一般情況都可以用下面的表格解釋(例外看下面的注意點!)

同步執(zhí)行 異步執(zhí)行
串行隊列 當前線程,一個一個執(zhí)行 其他線程,一個一個執(zhí)行
并行隊列 當前線程,一個一個執(zhí)行 開很多線程,一起執(zhí)行

注意點:經(jīng)過測試,得到的經(jīng)驗:(1)主線程中的其他隊列異步都能分出線程(系統(tǒng)線程資源沒用完前),(2)分線程中的其他隊列異步不一定分出線程;(3)主線程中的主隊列異步不能分出線程。

<a name="線程死鎖解析"></a>線程死鎖解析

一、串行隊列

  • 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}

解析:

在主隊列存在3個任務(wù)(任務(wù)1,同步任務(wù)2,任務(wù)3),按照正常的隊列的順序執(zhí)行順序應(yīng)該為
任務(wù)1-->同步任務(wù)2-->任務(wù)3;但是因為同步任務(wù)2的存在,導致主線程阻塞,且同步任務(wù)2被抽出重新入隊列,于是同步任務(wù)2,在主隊列中排在了任務(wù)3的后面。那么問題來了,現(xiàn)在有兩條線 1: 任務(wù)1-->同步任務(wù)2-->任務(wù)3 // 2: 任務(wù)1-->任務(wù)3-->同步任務(wù)2 于是主線程就會因為互相等待不知道先執(zhí)行哪個而完全阻塞。

如下圖所示

gcd-deadlock-1.png
  • 2、其他隊列的阻塞
    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)}

解析:
在主隊列存在3個任務(wù)(任務(wù)1,異步任務(wù),任務(wù)5),那么毫無疑問任務(wù)1最先執(zhí)行,因中間隔著是異步線程,故任務(wù)5可以和異步線程同時開始執(zhí)行;MyQueue串行隊列中存在3個任務(wù)(同步任務(wù)2,同步任務(wù),同步任務(wù)),任務(wù)2先執(zhí)行,然后遇到了同步操作,那么還是和上個問題一樣,同步任務(wù)3和任務(wù)4之間相互等待,造成 線程<NSThread: 0x60000007d200>的完全阻塞,程序停止;

如下圖所示

線程死鎖2.png
  • 3、錯覺:只要串行隊列中有同步操作,就會立馬死鎖?

看完第一個和第二個的例子是不是有一種錯覺:只要串行隊列中有同步操作,就會立馬死鎖?其實不然,下面的例子就證明了有串行同步操作也是可以的。

    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 ,且沒有推出程序
     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}

解析:
主隊列中3個任務(wù)(任務(wù)1,同步任務(wù),任務(wù)4),MyQueue隊列中兩個任務(wù)(任務(wù)2,任務(wù)3)。主線程首選執(zhí)行主隊列中的任務(wù)1,然后遇到了同步任務(wù),關(guān)鍵來了!因同步任務(wù)是MyQueue的全部任務(wù)集合,故不會和主隊列進行沖突,而是按照官方描述的,遇到同步任務(wù)要執(zhí)行完任務(wù)中的事情,于是任務(wù)2和任務(wù)3相繼被執(zhí)行,之后主隊列的任務(wù)4出隊列被執(zhí)行! 順序為:1->2->3->4。

如下圖所示

線程死鎖3.png
  • 4、串行隊列下到底怎樣的情況下才會造成死鎖?
    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}
    

解析:
主隊列中3個任務(wù)(任務(wù)1,同步任務(wù),任務(wù)5),MyQueue隊列中三個任務(wù)(任務(wù)2,同步任務(wù)3,任務(wù)4)。
主線程首先執(zhí)行任務(wù)1,之后遇到MyQueue的同步任務(wù),跳到MyQueue中執(zhí)行任務(wù)2,又遇到了MyQueue的同步任務(wù),那么同步任務(wù)中的任務(wù)3被推入隊列中,導致任務(wù)3跑到任務(wù)4的后面,導致了任務(wù)3和任務(wù)4的相互等待,造成死鎖!
我們回到第一個例子的代碼中再思考一下,和這段代碼有沒有共同點??其實我們可以理解為下面這段偽代碼

    //偽代碼,為了說明
    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)
    }

是不是明白了什么?整個主線程已經(jīng)是在串行同步的條件下了,
所以我么可以總結(jié)一下串行隊列的死鎖情況:串行隊列中有屬于自身隊列的同步操作,就會立馬死鎖!或者說 任何一個串行隊列,不能添加兩個本隊列的同步操作!

如下圖所示

線程死鎖4.png

二、并行隊列

  • 1、并行隊列下的同步
    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}

解析
主隊列中3個任務(wù)(任務(wù)1,同步任務(wù),任務(wù)5),MyQueue隊列中三個任務(wù)(任務(wù)2,同步任務(wù)3,任務(wù)4)。
主線程首先執(zhí)行任務(wù)1,遇到MyQueue的同步任務(wù)跳到MyQueue隊列中,執(zhí)行任務(wù)2,此時遇到了MyQueue自己隊列的同步任務(wù)3,因是并行情況所有所以直接執(zhí)行(ps:至于為什么直接過去了,我還沒找到原理般的解釋,先相信知乎上這篇文章上第一個回答所說的,如果大家知道原理,可以告訴我。)
執(zhí)行完成后再回到主隊列,執(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)}


解析
看圖就知道這個比較麻煩!主隊列中6個任務(wù)(任務(wù)1,異步任務(wù),任務(wù)5,休眠任務(wù),任務(wù)6,和后面入對的任務(wù)3),GlobleQueue隊列中三個任務(wù)(任務(wù)2,同步任務(wù),任務(wù)4)。主線程執(zhí)行任務(wù)1,遇到異步則分出線程執(zhí)行任務(wù)2,同時主線程繼續(xù)執(zhí)行任務(wù)5,所以任務(wù)2和5位置不一定。現(xiàn)在有兩條線程,主線程上進入了3秒休眠。分線程上因遇到主線程的同步任務(wù),主線程為串行同步隊列,故需要把任務(wù)3加入到主線程的隊尾,在任務(wù)6之后。休眠結(jié)束后,主線程繼續(xù)任務(wù)6和任務(wù)3,任務(wù)3執(zhí)行完之后,同步任務(wù)才得以完成,所以任務(wù)4才能執(zhí)行。有點繞,要消化一下!

如下圖所示

線程死鎖6.png

<a name="DispatchQueue的使用"></a>DispatchQueue的使用

初始化

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

屬性

  • label 隊列的標識 (required字段,下面都是option字段)
  • qos (quality of server) 服務(wù)質(zhì)量,也可以說是優(yōu)先級,優(yōu)先級高的得到的資源和權(quán)限就越高!下面為從高到低的優(yōu)先級
    1. userInteractive
    2. userInitiated
    3. default
    4. utility
    5. background
    6. unspecified
  • attributes
    1. concurrent: 并行自動模式
    2. initiallyInactive: 還是串行,但需要手動啟動 (如queue.activate())
  • autoreleaseFrequency
    1. inherit 繼承
    2. workItem 工作項目
    3. never
  • target 新的一個queue。注意:如果傳這個參數(shù)的話,前面的配置無效,全部按照新傳入的queue本身的配置。(親測)

<a name="Dispatchgroup的使用"></a>Dispatchgroup的使用

初始化

 let group = DispatchGroup.init()

實戰(zhàn)

下面的一段代碼很好的解決了多個網(wǎng)絡(luò)請求并行的問題。

    func testGroup() {
        
        let group = DispatchGroup.init()
        let queueOne = DispatchQueue.init(label: "queueOne",attributes:.concurrent)
        
        let firstTime = NSDate.init().timeIntervalSince1970
        print("主線程",Thread.current)
        print("開始時間",firstTime)
          //這里和下面的三段模擬日常的網(wǎng)絡(luò)請求
        group.enter()
        queueOne.async(group: group) {
            sleep(1) //模擬請求延遲
            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é)束時間",secTime)
            print("主線程",Thread.current)
        }
    }
    輸出:
    主線程 <NSThread: 0x60000006e800>{number = 1, name = main}
    開始時間 1487831647.5901
    1結(jié)束
    2結(jié)束
    3結(jié)束
    所有異步執(zhí)行結(jié)束時間 1487831650.66429
    主線程 <NSThread: 0x60000006e800>{number = 1, name = main}

解析
首先先創(chuàng)建一個group和一個并行的queue;然后往group里添加三段 并行queue異步的模擬網(wǎng)絡(luò)請求,分別延遲1s,2s,3s??摧敵瞿憔涂梢钥闯?,整個三段請求的時間是按照 請求時間最大的一段來決定的,所以是3s的時間。等所有的請求都完成之后,就會執(zhí)行g(shù)roup的notify回調(diào),傳人主線程,就可以刷新UI拉。

注意點
enter 和 leave實際執(zhí)行的次數(shù)得是1:1的,不然就會crash。所以我們平時就可以在網(wǎng)絡(luò)請求的成功和失敗block各放一個leave。因其要么失敗要么成功,比例還是1:1。

<a name="DispatchSourceTiemr的使用"></a>DispatchSourceTiemr的使用

初始化

(評論中有朋友遇到過 定時器不執(zhí)行的問題,是因為 沒把 定時器全局化 。如:)
 var timer:DispatchSourceTimer? 感謝XIAODAO同學 的建議,哈哈)
 let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags.strict, queue: DispatchQueue.main)
 當然也可以簡單的初始化:
 let timer = DispatchSource.makeTimerSource()//默認了主線程

注意點

timer要全局定義,上面那樣局部定義初始化是不會執(zhí)行的

屬性

  • flags 一般不用傳
  • queue 定時器運行的隊列,傳入主隊列 在主線程執(zhí)行;不傳則默認在分線程

主要方法

  • scheduleOneshot 循環(huán)單次

  • scheduleRepeating 重復循環(huán)

    兩個方法的屬性

    • wallDeadline 延遲執(zhí)行
    • leeway 允許的誤差
    • deadline 執(zhí)行的時間
    • interval 循環(huán)模式時的間隔時間

來看幾段代碼

單次執(zhí)行

    func runOnce()  {
        print("主線程",Thread.current)
        //創(chuàng)建主線程下的定時器
        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)建分線程下的定時器
        timer = DispatchSource.makeTimerSource()
        
        //循環(huán)執(zhí)行,馬上開始,間隔為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()
        
        //主隊列創(chuàng)建的情況下 如果在這里直接cancel(),handler里面的內(nèi)容是不能執(zhí)行的。如果是默認的分線程,則是可以的,至于為什么,我前面多線程的時候解釋過了哈。
         //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í)行一般來說可以分為兩大類

  • 1、 隊列直接asyncAfter
  • 2、 定時器

如下代碼所示

    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é)

針對本文的觀點,如有錯誤點,煩請指出!

本文引用以下文章的部分觀點:五個案例讓你明白GCD死鎖、關(guān)于iOS多線程,你看我就夠了

作者:燈泡蟲

郵箱:developer_yh@163.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容