Swift GCD之解決多個網絡請求的尷尬


  • 項目開發過程中, 經常遇到單個頁面需要發起多個網絡請求, 而且還需要控制執行順序或者所有請求結束后統一處理數據的情況. 最明顯的解決方案就是嵌套,但是如果請求多了,代碼確實有點惡心

DispatchQueue

  • 串行隊列的創建
let queue = DispatchQueue(label: "myQueue")
  • 並行隊列的創建, 參數說明
    • label: 隨意命名,隊列的標識
    • qos(服務質量): DispatchQoS.default DispatchQoS.background(后台執行) DispatchQoS.unspecified(不指定) DispatchQoS.userInitiated(用戶發起) 等
    • attributes: DispatchQueue.Attributes.concurrent(並行隊列) 不指定的情況是串行隊列
    • autoreleaseFrequency(自動釋放的頻度): inherit(繼承) workItem(工作組) never(從不)
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
  • 簡單使用
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)

queue.async {
    sleep(1)
    print("in queue 111")
}
queue.async {
    sleep(1)
    print("in queue 222")
}

print("finish")

DispatchGroup(進入正題)

線程組有兩種模式, 阻塞式(DispatchGroup-wait) 和 非阻塞式(DispatchGroup-notify)

wait

workingGroup.enter() 和 workingGroup.leave() 要成對出現, 只有leave()之后才會開始新的enter()
隊列中的請求任務是否順序執行, 取決於自己創建的隊列類型

// DispatchGroup-wait(阻塞)
let workingGroup = DispatchGroup()
let workingQueue = DispatchQueue(label: "request_queue") // 這個是串行的隊列, queue里面的任務會順序執行
//let workingQueue = DispatchQueue(label: "request_queue1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil) // 這個是並行隊列, queue里面的任務會同時執行

workingGroup.enter() // 開始
workingQueue.async {
    Thread.sleep(forTimeInterval: 2)
    print("接口 A 數據表請求完成")
    workingGroup.leave() // 結束
}

workingGroup.enter() // 開始
workingQueue.async {
    Thread.sleep(forTimeInterval: 1)
    print("接口 B 數據表請求完成")
    workingGroup.leave() // 結束
}
print("========= 我是最開始執行的 =========")

workingGroup.wait() // 阻塞, 直到Group中的任務都結束

print("接口 A 和接口 B 的數據都已經請求完畢, 開始合並兩個接口的數據")
print("========= 完美結束完美結束 =========")

notify

任務結束后, 通過通知回調的方式繼續執行, 同時不會阻塞線程

// DispatchGroup-notify(不阻塞)
let workingGroup1 = DispatchGroup()
//let workingQueue1 = DispatchQueue(label: "request_queue1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil) // 創建並行隊列
let workingQueue1 = DispatchQueue(label: "request_queue1") // 創建的默認隊列是串行隊列

workingGroup1.enter()
workingQueue1.async {
    Thread.sleep(forTimeInterval: 2)
    print("接口 C 數據表請求完成")
    workingGroup1.leave()
}

workingGroup1.enter()
workingQueue1.async {
    Thread.sleep(forTimeInterval: 1)
    print("接口 D 數據表請求完成")
    workingGroup1.leave()
}

print("↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 我是最開始執行的 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓")
workingGroup.notify(queue: workingQueue1) {
    print("接口 C 和接口 D 的數據都已經請求完畢, 開始合並兩個接口的數據")
}

print("驗證不阻塞 O(∩_∩)O哈哈~")

結束語

媽媽再也不用擔心我了

通過實際操作, 同時發起多個網絡請求, 發現以上的兩種方式, 只是控制了請求的順序, 並沒有控制返回數據的順序, 然后心態崩了, 只是崩了一小會兒, 繼續往下, 問題解決

swift中的信號量使用

// 創建初始值為0的信號量,這時代表是紅燈
let sema = DispatchSemaphore(value: 0)

// 讓信號量 -1,比如默認值時0, wait()以后就變成了 -1了,因此會等待
sema.wait()

// 讓信號量 +1, 當>0時就代表綠燈可以走了
sema.signal()
let workingGroup = DispatchGroup()
let workingQueue = DispatchQueue(label: "request_queue")

workingGroup.enter() // 開始
workingQueue.async {
    let sema = DispatchSemaphore(value: 0)
    let params = ["id": "458"]
    self.req1(params, sema: sema)
    sema.wait() // 等待任務結束, 否則一直阻塞
    workingGroup.leave() // 結束
}
workingGroup.enter() // 開始
workingQueue.async {
    let sema = DispatchSemaphore(value: 0)
    self.req2(self.productId, sema: sema)
    sema.wait() // 等待任務結束, 否則一直阻塞
    workingGroup.leave() // 結束
}

workingGroup.notify(queue: DispatchQueue.main) {
    // 全部調用完成后回到主線程,更新UI
}

private func req1(_ params: [String: Any], sema: DispatchSemaphore) {
    LBProductNetManager.req_addOrApplyNow(params: params, success: { (result) in
        // 信號量+1
        sema.signal()
    }) { (error) in
        // 失敗的時候也要+1, 否則會永遠阻塞了
        sema.signal()
    }
}

private func req2(_ productId: Int, sema: DispatchSemaphore) {
    LBProductNetManager.req_getHealthNotice(213, success: { (result) in
        // 信號量+1
        sema.signal()
    }) { (error) in
        // 失敗的時候也要+1, 否則會永遠阻塞了
        sema.signal()
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM