Swift多線程操作GCD使用總結(jié)

一、基本概念:同步, 異步, 并發(fā), 串行

同步和異步?jīng)Q定了要不要開(kāi)啟新的線程

1.同步(sync): 在當(dāng)前線程中執(zhí)行任務(wù), 不具備開(kāi)啟新線程的能力

2.異步(async): 在新的線程中執(zhí)行任務(wù), 具備開(kāi)啟新線程的能

并發(fā)和串行決定了任務(wù)的執(zhí)行方式

1.并發(fā)(Concurrent): 多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行

2.串行(Serial): 一個(gè)任務(wù)執(zhí)行完畢后, 再執(zhí)行下一個(gè)任務(wù)(順序執(zhí)行)

二、隊(duì)列創(chuàng)建

1、串行隊(duì)列

let queue1 = DispatchQueue.init(label: "Serial queue1")//label:隊(duì)列標(biāo)識(shí)符
如果想指定串行隊(duì)列的優(yōu)先級(jí), 可使用下面的方法來(lái)創(chuàng)建:
let queue2 = DispatchQueue.init(label: "QueueIdentifier", qos: DispatchQoS.userInitiated)

    參數(shù)qos: 用于指定隊(duì)列的優(yōu)先級(jí), 是個(gè)枚舉:
     
     .userInteractive //用戶交互(跟主線程一樣)
     .userInitiated   //用戶期望優(yōu)先級(jí)(不要放太耗時(shí)的操作)
     .default         //默認(rèn)的
     .utility         //公共的
     .background      //后臺(tái)
     .unspecified     //不指定
     
     優(yōu)先級(jí), 由上往下依次降低

最常用的串行隊(duì)列---主隊(duì)列
let mainQueue = DispatchQueue.main

2、并行隊(duì)列

和創(chuàng)建串行隊(duì)列的方法一樣, 只不過(guò)多了一個(gè)參數(shù)attributes, 傳 .concurrent 即可創(chuàng)建一個(gè)并行隊(duì)列

label 隊(duì)列的標(biāo)識(shí)符,方便調(diào)試
qos 用于指定隊(duì)列的優(yōu)先級(jí)
attributes 隊(duì)列的屬性
autoreleaseFrequency。顧名思義,自動(dòng)釋放頻率。有些隊(duì)列是會(huì)在履行完任務(wù)后自動(dòng)釋放的,有些比如Timer等是不會(huì)自動(dòng)釋放的,是需要手動(dòng)釋放。

let queue3 = DispatchQueue.init(label: "com.gcd.queue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)

在使用的時(shí)候, 我們一般不去創(chuàng)建并行隊(duì)列, 而是使用系統(tǒng)為我們提供的全局的并行隊(duì)列:

let queue4 = DispatchQueue.global()

也可以制定優(yōu)先級(jí)

let queue5 = DispatchQueue.global(qos: .default)

注意:
在創(chuàng)建串行并行隊(duì)列的時(shí)候, 參數(shù)attributes, 可以指定創(chuàng)建的是串行還是并行隊(duì)列, 他還有一個(gè)值: .initiallyInactive, 即: 創(chuàng)建的時(shí)候, 是處于不活躍狀態(tài), 即不會(huì)執(zhí)行任務(wù), 需要手動(dòng)調(diào)用activate()來(lái)激活隊(duì)列執(zhí)行任務(wù)

三、GCD常用的方法

1、開(kāi)線程異步執(zhí)行完耗時(shí)代碼,返回主線程刷新UI

func GCDTest1() {
        DispatchQueue.global().async {//并行、異步
             print("開(kāi)一條全局隊(duì)列異步執(zhí)行任務(wù)")
            DispatchQueue.main.async {//串行、異步
                print("在主隊(duì)列執(zhí)行刷新界面任務(wù)")
            }
        }
   }

2、等待異步執(zhí)行多個(gè)任務(wù)后, 再執(zhí)行下一個(gè)任務(wù)

    func GCDTest2() {
        let myQueue = DispatchQueue(label: "com.myQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)//并行隊(duì)列
        myQueue.async {//任務(wù)一
            for _ in 0...10 {
                 print("任務(wù)1......")
            }
        }
        myQueue.async {
            for _ in 0...5 {
                print("任務(wù)2++++++")
            }
        }
        // barrier 會(huì)等待上面執(zhí)行完畢再執(zhí)行下面的,會(huì)阻塞當(dāng)前線程
        myQueue.async(group: nil, qos: .default, flags: .barrier, execute: {//2.
            print("000000")
        })
        
        myQueue.async {
            print("111111")
        }
    }
    

打印輸出:
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)1......
任務(wù)2++++++
任務(wù)2++++++
任務(wù)2++++++
任務(wù)2++++++
任務(wù)2++++++
任務(wù)2++++++
000000
111111

3、延時(shí)提交任務(wù)

    func GCDTest3() {
        print("延時(shí)提交的任務(wù)--GCDTest3")
        //主隊(duì)列
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
            print("延時(shí)提交的任務(wù)--main")
        }
        
        //指定隊(duì)列
        let myQueue = DispatchQueue(label: "com.myQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)//并行隊(duì)列
        myQueue.asyncAfter(deadline: DispatchTime.now() + 3, execute: {
            print("延時(shí)提交的任務(wù)--myQueue")
        })
        
        let delayTime = DispatchTimeInterval.seconds(2)
        myQueue.asyncAfter(deadline: DispatchTime.now()+delayTime) {
            print("延時(shí)提交的任務(wù)--delayTime")

        }
    }

打印輸出:
延時(shí)提交的任務(wù)--GCDTest3
延時(shí)提交的任務(wù)--delayTime
延時(shí)提交的任務(wù)--main
延時(shí)提交的任務(wù)--myQueue

4、信號(hào)量--控制最大并發(fā)數(shù)

信號(hào)量的說(shuō)明:
GCD 信號(hào)量控制并發(fā) (dispatch_semaphore)
當(dāng)我們?cè)谔幚硪幌盗芯€程的時(shí)候,當(dāng)數(shù)量達(dá)到一定量,在以前我們可能會(huì)選擇使用NSOperationQueue來(lái)處理并發(fā)控制,但如何在GCD中快速的控制并發(fā)呢?答案就是dispatch_semaphore。
信號(hào)量是一個(gè)整形值并且具有一個(gè)初始計(jì)數(shù)值,并且支持兩個(gè)操作:信號(hào)通知和等待。當(dāng)一個(gè)信號(hào)量被信號(hào)通知,其計(jì)數(shù)會(huì)被增加。當(dāng)一個(gè)線程在一個(gè)信號(hào)量上等待時(shí),線程會(huì)被阻塞(如果有必要的話),直至計(jì)數(shù)器大于零,然后線程會(huì)減少這個(gè)計(jì)數(shù)。

在GCD中有三個(gè)函數(shù)是semaphore的操作,分別是:
1、dispatch_semaphore_create   創(chuàng)建一個(gè)semaphore
2、dispatch_semaphore_signal   發(fā)送一個(gè)信號(hào)
3、dispatch_semaphore_wait    等待信號(hào)

關(guān)于信號(hào)量,一般可以用停車來(lái)比喻:

*停車場(chǎng)剩余4個(gè)車位,那么即使同時(shí)來(lái)了4輛車也能停的下。如果此時(shí)來(lái)了5輛車,那么就有一輛需要等待。
*信號(hào)量的值就相當(dāng)于剩余車位的數(shù)目,dispatch_semaphore_wait 函數(shù)就相當(dāng)于來(lái)了一輛車,dispatch_semaphore_signal就相當(dāng)于走了一輛車
  
停車位的剩余數(shù)目在初始化的時(shí)候就已經(jīng)指明了(dispatch_semaphore_create(long value),
調(diào)用一次dispatch_semaphore_signal,剩余的車位就增加一個(gè);
調(diào)用一次dispatch_semaphore_wait剩余車位就減少一個(gè);
當(dāng)剩余車位為0時(shí),再來(lái)車(即調(diào)用dispatch_semaphore_wait)就只能等待。

有可能同時(shí)有幾輛車等待一個(gè)停車位。
有些車主沒(méi)有耐心,給自己設(shè)定了一段等待時(shí)間,這段時(shí)間內(nèi)等不到停車位就走了,如果等到了就開(kāi)進(jìn)去停車。
而有些車主就像把車停在這,所以就一直等下去.

下面我們逐一介紹三個(gè)函數(shù):

(1)dispatch_semaphore_create的聲明為:
dispatch_semaphore_t dispatch_semaphore_create(long value);

傳入的參數(shù)為long,輸出一個(gè)dispatch_semaphore_t類型且值為value的信號(hào)量。
值得注意的是,這里的傳入的參數(shù)value必須大于或等于0,否則dispatch_semaphore_create會(huì)返回NULL。
     
(2)dispatch_semaphore_signal的聲明為:
       
 long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
 
 這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值加1;
     
     
(3) dispatch_semaphore_wait的聲明為:
       
 long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
    
 這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值減1。
 這個(gè)函數(shù)的作用是這樣的,如果dsema信號(hào)量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語(yǔ)句,并且將信號(hào)量的值減1;
 如果desema的值為0,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout(注意timeout的類型為dispatch_time_t,不能直接傳入整形或float型數(shù)),如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號(hào)量,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1。
如果等待期間沒(méi)有獲取到信號(hào)量或者信號(hào)量的值一直為0,那么等到timeout時(shí),其所處線程自動(dòng)執(zhí)行其后語(yǔ)句。
     
(4)dispatch_semaphore_signal的返回值為long類型,
當(dāng)返回值為0時(shí)表示當(dāng)前并沒(méi)有線程等待其處理的信號(hào)量,其處理的信號(hào)量的值加1即可。
當(dāng)返回值不為0時(shí),表示其當(dāng)前有(一個(gè)或多個(gè))線程等待其處理的信號(hào)量,并且該函數(shù)喚醒了一個(gè)等待的線程(當(dāng)線程有優(yōu)先級(jí)時(shí),喚醒優(yōu)先級(jí)最高的線程;否則隨機(jī)喚醒)。

 dispatch_semaphore_wait的返回值也為long型。
 當(dāng)其返回0時(shí)表示在timeout之前,該函數(shù)所處的線程被成功喚醒。
 當(dāng)其返回不為0時(shí),表示timeout發(fā)生。  
 
 //每十個(gè)任務(wù)并發(fā)執(zhí)行
    func GCDTest4() {
        let group = DispatchGroup.init()
        let queue = DispatchQueue.global()
        //剩余10個(gè)車位
        let semaphore = DispatchSemaphore.init(value: 10)
        for i in 1...100 {
            
            //來(lái)了一輛車,信號(hào)量減1
            let result = semaphore.wait(timeout: .distantFuture)
            if result == .success {
                queue.async(group: group, execute: {
                    print("隊(duì)列執(zhí)行\(zhòng)(i)--\(Thread.current)")
                    //模擬執(zhí)行任務(wù)時(shí)間
                    sleep(3)
                    //延遲3s后,走了一輛車,信號(hào)量+1
                    semaphore.signal()
                })
            }
        }
        group.wait()
    }

5、Group的用法

你想某個(gè)任務(wù)在其他任務(wù)執(zhí)行之后再執(zhí)行, 或者必須某個(gè)任務(wù)執(zhí)行完,才能執(zhí)行下面的任務(wù), 可以使用DispatchGroup:

notify(依賴任務(wù))

    func GCDTest5() {
        let group = DispatchGroup.init()
        let myQueue = DispatchQueue(label: "com.myQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)//并行隊(duì)列
        myQueue.async(group: group, qos: .default, flags: []) {
            for _ in 0...10 {
                print("耗時(shí)任務(wù)1。。。。")
            }
        }
        myQueue.async(group: group, qos: .default, flags: []) {
            for _ in 0...10 {
                print("耗時(shí)任務(wù)2。。。。")
            }
        }
        //執(zhí)行完上面的兩個(gè)耗時(shí)操作, 回到myQueue隊(duì)列中執(zhí)行下一步的任務(wù)
        group.notify(queue: myQueue) {
             print("notify--回到該隊(duì)列中執(zhí)行")
        }
    }

打印輸出:
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
notify--回到該隊(duì)列中執(zhí)行

    func GCDTest6() {
        let queue = DispatchQueue(label: "queueName", attributes: .concurrent)
        queue.async {
            sleep(3)
            print("任務(wù) 1")
        }
        queue.async {
            sleep(2)
            print("任務(wù) 2")
        }
        queue.async {
            sleep(1)
            print("任務(wù) 3")
        }
        DispatchGroup.init().notify(qos: .default, flags: .barrier, queue: queue) {
            print("所有任務(wù)結(jié)束")
        }
        print("任務(wù)結(jié)束")
    }
    
打印輸出:

任務(wù)結(jié)束
任務(wù) 3
任務(wù) 2
任務(wù) 1
所有任務(wù)結(jié)束

wait(任務(wù)等待)

   func GCDTest7() {
        let group = DispatchGroup.init()
        let myQueue = DispatchQueue(label: "com.myQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)//并行隊(duì)列
        myQueue.async(group: group, qos: .default, flags: []) {
            for _ in 0...10 {
                print("耗時(shí)任務(wù)1。。。。")
            }
        }
        myQueue.async(group: group, qos: .default, flags: []) {
            for _ in 0...10 {
                print("耗時(shí)任務(wù)2。。。。")
                sleep(UInt32(1))//阻塞當(dāng)前線程。一旦看到這句話,當(dāng)前線程掛起給定時(shí)間后執(zhí)行下一句
            }
        }
        //等待上面任務(wù)執(zhí)行,會(huì)阻塞當(dāng)前線程,超時(shí)就執(zhí)行下面的,上面的繼續(xù)執(zhí)行??梢詿o(wú)限等待 .distantFuture
        let result = group.wait(timeout: .now() + 5.0)
        switch result {
        case .success:
            print("不超時(shí), 上面的兩個(gè)任務(wù)都執(zhí)行完")
        case .timedOut:
            print("超時(shí)了, 上面的任務(wù)還沒(méi)執(zhí)行完執(zhí)行這了")
        }
        print("接下來(lái)的操作")
    }
   

輸出打?。?br> 耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
超時(shí)了, 上面的任務(wù)還沒(méi)執(zhí)行完執(zhí)行這了
接下來(lái)的操作
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)2。。。。

如果將上面的 sleep(UInt32(1)) 注釋掉,則執(zhí)行不超時(shí)方法

輸出打?。?耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
耗時(shí)任務(wù)1。。。。
耗時(shí)任務(wù)2。。。。
不超時(shí), 上面的兩個(gè)任務(wù)都執(zhí)行完
接下來(lái)的操作

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

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

  • 本文首發(fā)于我的個(gè)人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「?jìng)魉烷T」[https...
    ITCharge閱讀 350,584評(píng)論 308 1,928
  • iOS多線程實(shí)踐中,常用的就是子線程執(zhí)行耗時(shí)操作,然后回到主線程刷新UI。在iOS中每個(gè)進(jìn)程啟動(dòng)后都會(huì)建立一個(gè)主線...
    jackyshan閱讀 1,572評(píng)論 2 12
  • 眉毛更簡(jiǎn)單的辦法是找準(zhǔn)這五個(gè)點(diǎn),初學(xué)者可以根據(jù)這五個(gè)點(diǎn)畫(huà)出淺淡的框,再用眉粉把邊緣過(guò)渡自然。
    睿睿Dora閱讀 171評(píng)論 0 0
  • 不知不覺(jué),柚子已經(jīng)成為一個(gè)小學(xué)生了。 由于他一直適應(yīng)性都還是不錯(cuò),加之溝通交流也比較好,所以,我和柚爸沒(méi)有太多的擔(dān)...
    周三三的月亮國(guó)閱讀 317評(píng)論 0 1
  • 2015.12開(kāi)始的,現(xiàn)在想來(lái)好遠(yuǎn)的樣子……還蠻喜歡這件事,可惜沒(méi)把本子帶去帝都~ 2017.01.23 21:29
    嫏嬛素素閱讀 208評(píng)論 0 2

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