swift多線(xiàn)程編程之GCD

什么是GCD

  • Grand Central Dispatch 是異步執(zhí)行任務(wù)的技術(shù)之一。開(kāi)發(fā)者只需要定義想要執(zhí)行的任務(wù),并追加到適當(dāng)?shù)腄ispatch Queue中,GCD就能生成必要的線(xiàn)程并執(zhí)行任務(wù)。我們看個(gè)經(jīng)典的例子。
DispatchQueue.global().async {
            
            /*
                這里執(zhí)行耗時(shí)操作
             */
            
            var sum = 0
            for i in 0...100 {
                print("global線(xiàn)程:\(Thread.current)")
                sum += i
            }
            
            /*
             這里刷新UI
             */
            
            DispatchQueue.main.async {
                print("main線(xiàn)程:\(Thread.current)")
                print(sum)
            }
        }
  • Cocoa 框架提供了NSObject類(lèi)的方法
self.performSelector(inBackground: #selector(doWork), with: nil)

    func doWork() {
        print("后臺(tái)處理中")
        
        self.performSelector(onMainThread: #selector(finishDone), with: nil, waitUntilDone: true)
    }
    
    func finishDone() {
        print("后臺(tái)處理完成")
    }

多線(xiàn)程編程

  • 我們知道代碼基本上都是從上到下順序執(zhí)行代碼。如果我們現(xiàn)在有個(gè)耗時(shí)的操作,那我們不想程序就卡在這里,我們就需要使用異步去處理,去處理下面的問(wèn)題?;蛘呶覀兺ǔL砑右粋€(gè)progress提示用戶(hù)正在處理某些東西。

  • 隊(duì)列(串行隊(duì)列,并行隊(duì)列)

串行隊(duì)列:?jiǎn)蝹€(gè)線(xiàn)程,等待上個(gè)任務(wù)執(zhí)行完畢,才往后執(zhí)行

并行隊(duì)列:多個(gè)線(xiàn)程,可以同時(shí)執(zhí)行

  • 如其名稱(chēng)所示,是執(zhí)行處理等待的隊(duì)列。

  • 兩個(gè)關(guān)鍵隊(duì)列 main, global
    主隊(duì)列,和全局隊(duì)列。我們通常將我們要執(zhí)行的操作,追加到這個(gè)隊(duì)列后。async, sync 來(lái)說(shuō)明這個(gè)block是異步還是同步執(zhí)行。

  • 測(cè)試main, 和global

DispatchQueue.main.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        }
        DispatchQueue.main.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        }
        DispatchQueue.main.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        }
        DispatchQueue.main.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
        }

-打印結(jié)果(main隊(duì)列只有一個(gè)線(xiàn)程所以是串行隊(duì)列)

  1. 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->1
  2. 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->2
  3. 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->3
  4. 當(dāng)前線(xiàn)程<NSThread: 0x7b257510>{number = 1, name = main}------->4
  • 測(cè)試global隊(duì)列
DispatchQueue.global().async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        }
        DispatchQueue.global().async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        }
        DispatchQueue.global().async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        }
        DispatchQueue.global().async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
        }
  • 打印結(jié)果(global隊(duì)列多個(gè)線(xiàn)程執(zhí)行,并行執(zhí)行是并行隊(duì)列)
  1. 當(dāng)前線(xiàn)程<NSThread: 0x7d160c10>{number = 3, name = (null)}------->3
  2. 當(dāng)前線(xiàn)程<NSThread: 0x7bfa9dd0>{number = 2, name = (null)}------->1
  3. 當(dāng)前線(xiàn)程<NSThread: 0x7d15de60>{number = 4, name = (null)}------->4
  4. 當(dāng)前線(xiàn)程<NSThread: 0x7d153b90>{number = 5, name = (null)}------->2

如何創(chuàng)建隊(duì)列

  • 當(dāng)然除了系統(tǒng)提供給我們的這兩個(gè)隊(duì)列,我們還可以自己創(chuàng)建隊(duì)列。

  • 串行隊(duì)列

let queue = DispatchQueue(label: "myQueue")
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
            }
// 打印結(jié)果
1. 當(dāng)前線(xiàn)程<NSThread:0x7c0825f0>{number = 2, name = (null)}------->1
2. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->2
3. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->3
4. 當(dāng)前線(xiàn)程<NSThread: 0x7c0825f0>{number = 2, name = (null)}------->4
  • 并行隊(duì)列
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
  • qos 隊(duì)列的優(yōu)先等級(jí)
  1. User Interactive 和用戶(hù)交互相關(guān),比如動(dòng)畫(huà)等等優(yōu)先級(jí)最高。比如用戶(hù)連續(xù)拖拽的計(jì)算
  2. User Initiated 需要立刻的結(jié)果,比如push一個(gè)ViewController之前的數(shù)據(jù)計(jì)算
  3. Utility 可以執(zhí)行很長(zhǎng)時(shí)間,再通知用戶(hù)結(jié)果。比如下載一個(gè)文件,給用戶(hù)下載進(jìn)度。
  4. Background 用戶(hù)不可見(jiàn),比如在后臺(tái)存儲(chǔ)大量數(shù)據(jù)
  • attributes 是并行的還是串行的枚舉值(concurrent:并行的, initiallyInactive:串行的)

  • autoreleaseFrequency 隊(duì)列的釋放規(guī)則

  • 切換隊(duì)列

let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        }
        //切換隊(duì)列
        queue.setTarget(queue: DispatchQueue.main)
        
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
        }
  • 執(zhí)行結(jié)果
  1. 當(dāng)前線(xiàn)程<NSThread: 0x7bc2d970>{number = 2, name = (null)}------->2
  2. 當(dāng)前線(xiàn)程<NSThread: 0x7b63fe10>{number = 3, name = (null)}------->1
  3. 當(dāng)前線(xiàn)程<NSThread: 0x7b6215b0>{number = 1, name = main}------->3
  4. 當(dāng)前線(xiàn)程<NSThread: 0x7b6215b0>{number = 1, name = main}------->4
  • 延時(shí)任務(wù)
DispatchQueue.main.asyncAfter(deadline: .now() + DispatchTimeInterval.seconds(3)) { //不是三秒后執(zhí)行該任務(wù),而是三秒后添加到改隊(duì)列
            //執(zhí)行的代碼
        }
  • 隊(duì)列組

  • 對(duì)于串行隊(duì)列來(lái)說(shuō)最后一個(gè)加入的,肯定是最后一個(gè)結(jié)束。但是并行隊(duì)列來(lái)說(shuō)我們?cè)趺粗?,這些任務(wù)都完成了吶。

  • 看看串行隊(duì)列的處理方式

let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.initiallyInactive, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        }
        queue.async {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
            DispatchQueue.main.async {
                print("執(zhí)行完了")
            }
        }
  • 打印結(jié)果
  1. 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->1
  2. 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->2
  3. 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->3
  4. 當(dāng)前線(xiàn)程<NSThread: 0x7a66cea0>{number = 2, name = (null)}------->4
  5. 執(zhí)行完了
  • 并行隊(duì)列我們可以使用group去解決這個(gè)問(wèn)題
let group = DispatchGroup()
        let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
        })
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
        })
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
        })
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->4")
        })
        let workItem = DispatchWorkItem { 
            print("執(zhí)行完成")
        }  group.notify(queue: queue, work: workItem)
  • 打印結(jié)果
  1. 當(dāng)前線(xiàn)程<NSThread: 0x78e49ae0>{number = 2, name = (null)}------->2
  2. 當(dāng)前線(xiàn)程<NSThread: 0x790a4710>{number = 3, name = (null)}------->1
  3. 當(dāng)前線(xiàn)程<NSThread: 0x78e53d00>{number = 5, name = (null)}------->3
  4. 當(dāng)前線(xiàn)程<NSThread: 0x78e43b60>{number = 4, name = (null)}------->4
  5. 執(zhí)行完成
  • group中wait方法

和notify不同的是wait是等待一段時(shí)間,不管隊(duì)列的任務(wù)是否都完成了,都會(huì)調(diào)用。

  • DispatchWorkItem
let workItem = DispatchWorkItem { 
            print("開(kāi)始干活\(Thread.current)")
        }
//        workItem.perform() //立即執(zhí)行, 放到了當(dāng)前main隊(duì)列中
//        DispatchQueue.global().async(execute: workItem) //放到global隊(duì)列中
  • group的enter 和 leave
        let group = DispatchGroup()
        let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
            queue.async(execute: {
                print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
            })
            queue.async(execute: {
                print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
            })
        })
        let workItem = DispatchWorkItem {
            print("done")
        }
        group.notify(queue: queue, work: workItem)
  • 打印結(jié)果
  1. 當(dāng)前線(xiàn)程<NSThread: 0x7b967590>{number = 2, name = (null)}------->1
  2. done
  3. 當(dāng)前線(xiàn)程<NSThread: 0x797807d0>{number = 1, name = main}------->2
  4. 當(dāng)前線(xiàn)程<NSThread: 0x7b8bd8b0>{number = 3, name = (null)}------->3
  • 我們?cè)囅氘?dāng)我們的并行隊(duì)列里還有其他的并行隊(duì)列,我們想等他們都操作完之后,在執(zhí)行下一步,該怎么辦。
let group = DispatchGroup()
        let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil)
        queue.async(group: group, execute: {
            print("當(dāng)前線(xiàn)程\(Thread.current)------->1")
            
            group.enter()
            queue.async(execute: {
                group.leave()
                print("當(dāng)前線(xiàn)程\(Thread.current)------->2")
            })
            
            group.enter()
            queue.async(execute: {
                print("當(dāng)前線(xiàn)程\(Thread.current)------->3")
                group.leave()
            })
            
        })
        let workItem = DispatchWorkItem {
            print("done")
        }
        group.notify(queue: queue, work: workItem)
  1. 當(dāng)前線(xiàn)程<NSThread: 0x7a95e9e0>{number = 2, name = (null)}------->1
  2. 當(dāng)前線(xiàn)程<NSThread: 0x7a95e9e0>{number = 2, name = (null)}------->2
  3. 當(dāng)前線(xiàn)程<NSThread: 0x7a963a70>{number = 3, name = (null)}------->3
  4. done
  • 并行隊(duì)列處理數(shù)據(jù)的時(shí)候出現(xiàn)的問(wèn)題
var array: [Int] = []
        
        for i in 0...100 {
            DispatchQueue.global().async(execute: { 
                array.append(i)
            })
        }
  • 多個(gè)線(xiàn)程去更新array時(shí)就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤。同一個(gè)線(xiàn)程訪(fǎng)問(wèn)了同一塊地址內(nèi)存,同時(shí)往里面進(jìn)行寫(xiě)數(shù)據(jù)。
Demo(61868,0x38bc1c0) malloc: *** error for object 0x7ac34424: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
(lldb)
  1. 使用信號(hào)量解決
var array: [Int] = []
        let semaphore = DispatchSemaphore(value: 1)
        for i in 0...100 {
            DispatchQueue.global().async(execute: {
                
                /*
                    wait 等待信號(hào)量semaphore >= 1時(shí)執(zhí)行
                    wait返回時(shí) semaphore 減1
                 */
                _ = semaphore.wait(wallTimeout: .distantFuture)
                
                array.append(i) //只會(huì)同時(shí)被一個(gè)線(xiàn)程訪(fǎng)問(wèn)
                
                /*
                 signal semaphore 加1
                 */
                semaphore.signal()
            })
        }
        DispatchQueue.main.async {
            print(array.count)
        }
  • 使用互斥鎖
let lock = NSLock()
        var array: [Int] = []
        
        for i in 0...100 {
            DispatchQueue.global().async(execute: {
                lock.lock()
                array.append(i)
                lock.unlock()
            })
        }
        DispatchQueue.main.async {
            print(array.count)
        }

死鎖(線(xiàn)程之間互相等待對(duì)方執(zhí)行完之后才能接著執(zhí)行)

  • main隊(duì)列里執(zhí)行下面代碼造成死鎖。使用sync編程時(shí)要注意死鎖的發(fā)生。
DispatchQueue.main.sync {
            print("死鎖了")
        }

DispatchSource 調(diào)度源

它的作用是當(dāng)有一些特定的較底層的系統(tǒng)事件發(fā)生時(shí),調(diào)度源會(huì)捕捉到這些事件,然后可以做其他的邏輯處理,調(diào)度源有多種類(lèi)型,分別監(jiān)聽(tīng)對(duì)應(yīng)類(lèi)型的系統(tǒng)事件。我們來(lái)看看它都有哪些類(lèi)型
  1. DISPATCH_SOURCE_TYPE_DATA_ADD:屬于自定義事件,可以通過(guò)dispatch_source_get_data函數(shù)獲取事件變量數(shù)據(jù),在我們自定義的方法中可以調(diào)用dispatch_source_merge_data函數(shù)向Dispatch Source設(shè)置數(shù)據(jù),下文中會(huì)有詳細(xì)的演示。
  2. DISPATCH_SOURCE_TYPE_DATA_OR:屬于自定義事件,用法同上面的類(lèi)型一樣。
  3. DISPATCH_SOURCE_TYPE_MACH_SEND:Mach端口發(fā)送事件。
  4. DISPATCH_SOURCE_TYPE_MACH_RECV:Mach端口接收事件。
  5. DISPATCH_SOURCE_TYPE_PROC:與進(jìn)程相關(guān)的事件。
  6. DISPATCH_SOURCE_TYPE_READ:讀文件事件。
  7. DISPATCH_SOURCE_TYPE_WRITE:寫(xiě)文件事件。
  8. DISPATCH_SOURCE_TYPE_VNODE:文件屬性更改事件。
  9. DISPATCH_SOURCE_TYPE_SIGNAL:接收信號(hào)事件。
  10. DISPATCH_SOURCE_TYPE_TIMER:定時(shí)器事件。
  11. DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:內(nèi)存壓力事件。
  • 看例子每隔一秒執(zhí)行操作
private var timer: DispatchSourceTimer!

        self.timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags.strict, queue: DispatchQueue.global())
        self.timer.scheduleRepeating(deadline: .now(), interval: DispatchTimeInterval.seconds(1)) //每隔一秒執(zhí)行下面的操作
        self.timer.setEventHandler {
            DispatchQueue.main.async(execute: {
                //回到主線(xiàn)程刷新UI
                print(Date())
            })
        }
        self.timer.resume()
  • 你可以使用改裝一下做個(gè)倒計(jì)時(shí)。當(dāng)獲取激活碼時(shí)。

  • 假如我們有5個(gè)線(xiàn)程去下載,我們想知道這五個(gè)線(xiàn)程的下載進(jìn)度。

private var dispatchDataSource: DispatchSourceUserDataAdd!

        var progress: UInt = 0
        self.dispatchDataSource = DispatchSource.makeUserDataAddSource(queue: DispatchQueue.main)
        self.dispatchDataSource.setEventHandler { 
            let data = UInt(self.dispatchDataSource.data) //獲取數(shù)據(jù)中心的data
            progress += data
            print("上傳任務(wù)進(jìn)度:\(progress)")
        }
        self.dispatchDataSource.resume()
        
        for _ in 0...5 {
            DispatchQueue.global().async(execute: {
                print("異步處理任務(wù)")
                
                self.dispatchDataSource.add(data: 1) //將數(shù)組添加數(shù)據(jù)中心
            })
        }
  • 打印結(jié)果
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
異步處理任務(wù)
上傳任務(wù)進(jìn)度:6
最后編輯于
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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