GCD總結(jié)

一早像、概念

隊(duì)列(DispatchQueue)

Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system. Except for the dispatch queue representing your app's main thread, the system makes no guarantees about which thread it uses to execute a task.

調(diào)度隊(duì)列是一個(gè)FIFO(先進(jìn)先出)隊(duì)列,應(yīng)用程序可以以block對(duì)象的形式向其提交任務(wù)。dispatch隊(duì)列可以串行或并發(fā)執(zhí)行任務(wù)橘洞。提交到調(diào)度隊(duì)列的任務(wù)執(zhí)行在系統(tǒng)管理的線程池上绍载。除了代表應(yīng)用程序主線程的調(diào)度隊(duì)列外割按,系統(tǒng)不保證是哪條線程執(zhí)行任務(wù)

由上面看出隊(duì)列有串行、并行兩種此蜈,那么他們有何區(qū)別呢?

串行隊(duì)列(Serial Dispatch Queue)

每次只執(zhí)行一個(gè)任務(wù)噪生。任務(wù)是按照進(jìn)入隊(duì)列的順序執(zhí)行的裆赵,并且只會(huì)開啟一條線程

并發(fā)隊(duì)列(Concurrent Dispatch Queue)

多個(gè)任務(wù)并發(fā)執(zhí)行,且可以開啟多條線程

任務(wù)

任務(wù)就是我們?cè)诰€程中執(zhí)行的那段代碼跺嗽,分為同步任務(wù)(sync)和異步任務(wù)(async)战授。其主要區(qū)別是:是否等待任務(wù)執(zhí)行完,以及是否具備開啟新線程的能力

同步任務(wù)(sync)

同步任務(wù)添加到指定的隊(duì)列中桨嫁,在它未執(zhí)行完成之前植兰,會(huì)一直等待,直到任務(wù)完成后才會(huì)返回璃吧;只能在當(dāng)前線程中執(zhí)行任務(wù)楣导,不具備開啟新線程的能力

異步任務(wù)(async)

異步任務(wù)添加到指定的隊(duì)列中,它不會(huì)等待任務(wù)完成畜挨,就會(huì)返回筒繁∝迹可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

1.串行隊(duì)列同步任務(wù)

  override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 創(chuàng)建串行隊(duì)列
        let queue = DispatchQueue(label: "myQueue", qos: .default)
        queue.sync {
            Thread.sleep(forTimeInterval: 1)
            print("run task1 thread \(Thread.current)")
        }      
        queue.sync {
            Thread.sleep(forTimeInterval: 0.5)
            print("run task2 thread \(Thread.current)")
        }
 }

輸出:

run task1 thread <NSThread: 0x6000012059c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000012059c0>{number = 1, name = main}

2.串行隊(duì)列異步任務(wù)

  override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 創(chuàng)建串行隊(duì)列
        let queue = DispatchQueue(label: "myQueue", qos: .default)
        queue.async {
            Thread.sleep(forTimeInterval: 1)
            print("run task1 thread \(Thread.current)")
        }      
        queue.async {
            Thread.sleep(forTimeInterval: 0.5)
            print("run task2 thread \(Thread.current)")
        }
  }

輸出:

run task1 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600001b33dc0>{number = 3, name = (null)}

3.并發(fā)隊(duì)列同步任務(wù)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // 全局并行隊(duì)列
        let queue = DispatchQueue.global()
        queue.sync {
            Thread.sleep(forTimeInterval: 1)
            print("run task1 thread \(Thread.current)")
        }
        queue.sync {
            Thread.sleep(forTimeInterval: 0.5)
            print("run task2 thread \(Thread.current)")
        }
        queue.sync {
            print("run task3 thread \(Thread.current)")
        }
    }

輸出:

run task1 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task2 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}
run task3 thread <NSThread: 0x6000029b59c0>{number = 1, name = main}

4.并發(fā)隊(duì)列異步任務(wù)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let queue = DispatchQueue.global()
        queue.async {
            print("run task1 thread \(Thread.current)")
        }
        queue.async {
            print("run task2 thread \(Thread.current)")
        }
        queue.async {
            print("run task3 thread \(Thread.current)")
        }
    }

輸出:

run task3 thread <NSThread: 0x600003589100>{number = 3, name = (null)}
run task2 thread <NSThread: 0x600003589140>{number = 4, name = (null)}
run task1 thread <NSThread: 0x6000035b76c0>{number = 5, name = (null)}
GCD死鎖

GCD死鎖就是毡咏,兩個(gè)任務(wù)相互等待驮宴,從而造成死鎖
死鎖原因??jī)蓚€(gè)任務(wù)相互等待
例:

override func viewDidLoad() {
     super.viewDidLoad()
     // Do any additional setup after loading the view, typically from a nib.

     print("start")
     DispatchQueue.main.sync {   // 死鎖
         print("ahaha")
     }
     print("end")
}

輸出:
    start

怎么理解血当?
我們先看下面的代碼

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        print("Hello, World!")
        run()
        print("game over")
    }
    
    func run() {
        print("do func run")
    }

輸出:

Hello, World!
do func run
game over

我們?cè)趯W(xué)校的時(shí)候老師告訴我們代碼是按順序執(zhí)行幻赚,所以很好理解上面的輸出,即執(zhí)行完打印hello world臊旭,就執(zhí)行run函數(shù)落恼,當(dāng)執(zhí)行完run后再打印game over。再次印證了代碼是一步一步執(zhí)行的离熏,很好請(qǐng)記住這句佳谦,很重要

回到死鎖的例子來分析原因滋戳。首先執(zhí)行start钻蔑,這毫無疑問;然后執(zhí)行同步任務(wù)奸鸯,同步任務(wù)的特點(diǎn)是執(zhí)行完同步任務(wù)咪笑,再返回,意思是要等這個(gè)任務(wù)執(zhí)行完才會(huì)執(zhí)行end娄涩;然后我們來看下這個(gè)任務(wù)是加在哪個(gè)隊(duì)列窗怒?一看是DispatchQueue.main即主隊(duì)列,而當(dāng)前主隊(duì)列是在執(zhí)行viewDidLoad蓄拣,也就是說這個(gè)同步任務(wù)是加在viewDidLoad之后扬虚。因?yàn)橹麝?duì)列是一個(gè)串行隊(duì)列,也就是說球恤,隊(duì)列里的任務(wù)是一個(gè)接著一個(gè)執(zhí)行辜昵,只有前一個(gè)任務(wù)執(zhí)行完,才會(huì)執(zhí)行下一個(gè)咽斧。那么問題就出現(xiàn)了代碼執(zhí)行到DispatchQueue.main.sync這里時(shí)堪置,主隊(duì)列阻塞了,要等到任務(wù)執(zhí)行完张惹,但是主隊(duì)列的任務(wù)即viewDidLoad需要等到該任務(wù)執(zhí)行完才會(huì)執(zhí)行下一個(gè)任務(wù)舀锨,這就造成了兩個(gè)任務(wù)互相等待,從而死鎖

而下面這個(gè)例子將再次印證代碼是一步一步執(zhí)行的

override func viewDidLoad() {
     super.viewDidLoad()
     // Do any additional setup after loading the view, typically from a nib.

     print("start")
     DispatchQueue.global().sync {   // 不死鎖
         print("ahaha")
     }
     print("end")
}

輸出:
    start
    ahaha
    end

這為什么沒有死鎖呢诵叁?因?yàn)檫@個(gè)同步任務(wù)是加在全局并發(fā)隊(duì)列里雁竞,不是加在主隊(duì)列,因此當(dāng)全局并發(fā)隊(duì)列執(zhí)行完這個(gè)任務(wù),就會(huì)執(zhí)行end

二碑诉、GCD相關(guān)函數(shù)

2.1GCD柵欄方法
執(zhí)行隊(duì)列中在barrier之前加入的任務(wù)彪腔,才會(huì)執(zhí)行在barrier之后加入的任務(wù)。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        let queue = DispatchQueue(label: "hahah", qos: .default, attributes: .concurrent)
        queue.async {
            print("begin task1..... will sleep 2 second")
            Thread.sleep(forTimeInterval: 2)
            print("end task1")
        }
        queue.async {
            print("begin task2.....will sleep 3 second")
            Thread.sleep(forTimeInterval: 3)
            print("end task2")
        }
        
        let item = DispatchWorkItem(qos: .default, flags: .barrier) {
            print("begin barrier sleep 2 second")
            Thread.sleep(forTimeInterval: 2)
            print("end barrier")
        }
        queue.async(execute: item)
        
        queue.async {
            print("task3....")
        }
    }

//輸出:
    begin task1..... will sleep 2 second
    begin task2.....will sleep 3 second
    end task1
    end task2
    begin barrier sleep 2 second
    end barrier
    task3....

2.2 group notify
group.notify當(dāng)group中的任務(wù)執(zhí)行完之后才會(huì)執(zhí)行并且不會(huì)阻塞隊(duì)列

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let queue = DispatchQueue.global()
        let group = DispatchGroup()
        let download = DispatchWorkItem {
            print("task1...")
        }
        queue.async(group: group, execute: download)
        let saveDb = DispatchWorkItem {
            print("task2...")
        }
        queue.async(group: group, execute: saveDb)
        group.notify(queue: DispatchQueue.main) {
            print("all finish")
        }
        print("end")
    }
//輸出:
    task2...
    end
    task1...
    all finish

如果task是異步任務(wù)进栽,那么notify的任務(wù)就不會(huì)等到task1德挣、task2完成,那要怎么做了快毛,使用enter格嗅、level,注意enter唠帝、level需要成對(duì)出現(xiàn)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        let queue = DispatchQueue.global()
        let group = DispatchGroup()
        group.enter()
        let download = DispatchWorkItem {
            queue.async {
                Thread.sleep(forTimeInterval: 2)
                print("download task1 fininsh")
                group.leave()
            }
        }
        queue.async(group: group, execute: download)
        group.enter()
        let saveDb = DispatchWorkItem {
            queue.async {
                Thread.sleep(forTimeInterval: 1.5)
                print("download task2 finish")
                group.leave()
            }
        }
        queue.async(group: group, execute: saveDb)
        group.notify(queue: DispatchQueue.main) {
            print("all finish")
        }
    }
// 輸出
download task2 finish
download task1 fininsh
all finish

2.3信號(hào)量
使用DispatchSemaphore創(chuàng)建信號(hào)量
調(diào)用single()會(huì)使信號(hào)量+1
調(diào)用wait()會(huì)使信號(hào)量-1屯掖,當(dāng)信號(hào)量小于0時(shí)阻塞當(dāng)前線程
因此可以使用信號(hào)量可以讓多個(gè)異步任務(wù)達(dá)到同步執(zhí)行的效果

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        let semaphore = DispatchSemaphore(value: 0)
        DispatchQueue.global().async {
            print("run task1")
            Thread.sleep(forTimeInterval: 2)
            semaphore.signal()
        }
        _ = semaphore.wait(timeout: DispatchTime.distantFuture)
        DispatchQueue.global().async {
            print("run task2")
            Thread.sleep(forTimeInterval: 1)
            semaphore.signal()
        }
        _ = semaphore.wait(timeout: DispatchTime.distantFuture)
        DispatchQueue.global().async {
            print("run task3")
        }
    }
//輸出:
run task1
run task2
run task3

2.4延遲執(zhí)行

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        let queue = DispatchQueue.global()
        let time = DispatchTime.now() + 3
        queue.asyncAfter(deadline: time) {
            print("after 3 sencond")
        }
    }

上面的3也可以使用DispatchTimeInterval的second、milliseconds襟衰、microseconds贴铜、nanoseconds來代替

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瀑晒,隨后出現(xiàn)的幾起案子绍坝,更是在濱河造成了極大的恐慌,老刑警劉巖苔悦,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轩褐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡玖详,警方通過查閱死者的電腦和手機(jī)把介,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竹宋,“玉大人劳澄,你說我怎么就攤上這事地技◎谄撸” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵莫矗,是天一觀的道長飒硅。 經(jīng)常有香客問我,道長作谚,這世上最難降的妖魔是什么三娩? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮妹懒,結(jié)果婚禮上雀监,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好会前,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布好乐。 她就那樣靜靜地躺著,像睡著了一般瓦宜。 火紅的嫁衣襯著肌膚如雪蔚万。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天临庇,我揣著相機(jī)與錄音反璃,去河邊找鬼。 笑死假夺,一個(gè)胖子當(dāng)著我的面吹牛淮蜈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播已卷,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼礁芦,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了悼尾?” 一聲冷哼從身側(cè)響起柿扣,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闺魏,沒想到半個(gè)月后未状,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡析桥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年司草,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泡仗。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埋虹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娩怎,到底是詐尸還是另有隱情搔课,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布截亦,位于F島的核電站爬泥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏崩瓤。R本人自食惡果不足惜袍啡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望却桶。 院中可真熱鬧境输,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窗悯,卻和暖如春区匣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒋院。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國打工亏钩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欺旧。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓姑丑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辞友。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栅哀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

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