關(guān)于多個網(wǎng)絡(luò)請求同步的一些總結(jié)

等待多個并發(fā)請求同步回調(diào)

例如同時(shí)發(fā)起網(wǎng)絡(luò)請求A,網(wǎng)絡(luò)請求B,網(wǎng)絡(luò)請求C,需等待A,B,C都返回了才進(jìn)行回調(diào)。曾經(jīng)是利用 block 嵌套,就是A回調(diào)嵌B,B回調(diào)嵌C,十分膚淺的做法╮(╯▽╰)╭
這里介紹 GCD 中的兩種方法,第一種是利用 DispatchGroupenterleave,第二種是利用 DispatchSemaphore 信號量。
以下代碼是基于 swift 3.0.2。
首先先創(chuàng)建一個模擬請求。

    // 假裝是網(wǎng)絡(luò)請求
    func networkRequest(sleepTime: Int, closure: @escaping ()->Void) -> Void {
        DispatchQueue.global().async { 
            Thread.sleep(forTimeInterval: TimeInterval(sleepTime))
            // 假裝是成功回調(diào)
            closure()
        }
    }

第一種方法利用 enter&leave。需要注意的是 enter&leave 必須成對出現(xiàn),enter 少了會崩潰,leave 少了則永遠(yuǎn)不會執(zhí)行 notify 函數(shù)。
這種方法的原理就是:

  1. 請求前 enter。
  2. 請求完成后 leave
    // 利用 enter/leave 來控制
    func gcd_group_enter_leave() {
        let group = DispatchGroup.init()
        let queue = DispatchQueue.global()
        
        queue.async(group: group) {
            group.enter()
            print("1 start")
            self.networkRequest(sleepTime:1, closure: {
                print("1 end")
                group.leave()
            })
        }
        
        queue.async(group: group) {
            group.enter()
            print("2 start")
            self.networkRequest(sleepTime:2, closure: {
                print("2 end")
                group.leave()
            })
        }
        
        queue.async(group: group) {
            group.enter()
            print("3 start")
            self.networkRequest(sleepTime:2, closure: {
                print("3 end")
                group.leave()
            })
        }
        
        queue.async(group: group) {
            group.enter()
            print("4 start")
            self.networkRequest(sleepTime:2, closure: {
                print("4 end")
                group.leave()
            })
        }
        
        group.notify(queue: queue) { // 所有組完成后回調(diào)
            print("all done")
        }
    }

第二種方法是利用信號量的 wait&signal,簡單來說就是:signal 就是釋放信號即信號量 +1,wait 就是等待信號即信號量-1。wait&signal也是必須成對出現(xiàn)。
這種方法的原理就是:

  1. 創(chuàng)建0信號量。
  2. 請求完成釋放信號,使得信號量+1。
  3. notify 的回調(diào)操作前加入 wait 操作(多少請求就加多少 wait,這要做為了得到足夠的信號量才能執(zhí)行 wait 下面的代碼)。
    // 利用 semaphore 來控制
    func gcd_semaphore_wait_signal() {
        let semaphore = DispatchSemaphore.init(value: 0)
        let group = DispatchGroup.init()
        let queue = DispatchQueue.global()
        
        queue.async(group: group) {
            self.networkRequest(sleepTime:1, closure: {
                print("1")
                semaphore.signal()
            })
        }
        
        queue.async(group: group) {
            self.networkRequest(sleepTime:2, closure: {
                print("2")
                semaphore.signal()
            })
        }
        
        queue.async(group: group) {
            self.networkRequest(sleepTime:2, closure: {
                print("3")
                semaphore.signal()
            })
        }
        
        group.notify(queue: queue) {
            semaphore.wait()
            semaphore.wait()
            semaphore.wait()
            print("all done")
        }
    }

多個相關(guān)請求順序執(zhí)行

有時(shí)候開發(fā)中也會遇到利用網(wǎng)絡(luò)請求A返回的的數(shù)據(jù)來進(jìn)行網(wǎng)絡(luò)請求B,以前也是十分膚淺地利用嵌套,雖然簡單可行而且可以減少中間變量,但是出現(xiàn)過多嵌套代碼不易debug。這里也是可以利用 DispatchSemaphore 信號量。網(wǎng)絡(luò)請求A完成后 signal,而網(wǎng)絡(luò)請求B發(fā)起前 wait。

    // 利用 semaphore 來控制
    func gcd_line_request() {
        let semaphore = DispatchSemaphore.init(value: 0)
        let group = DispatchGroup.init()
        let queue = DispatchQueue.global()
        
        queue.async(group: group) {
            self.networkRequest(sleepTime:1, closure: {
                print("1")
                semaphore.signal()
            })
        }
        
        queue.async(group: group) {
            semaphore.wait()
            self.networkRequest(sleepTime:2, closure: {
                print("2")
            })
        }
    }

當(dāng)然也可以利用RAC,在A請求完成后發(fā)送信號喚醒B執(zhí)行即可,還可以傳遞參數(shù)。

  // 利用 rac 來控制
  func rac_request() {

      let (requestA, observerA) = Signal<Bool, NoError>.pipe()

      self.networkRequest(sleepTime: 1) {
          print("網(wǎng)絡(luò)請求A完成")
          observerA.send(value: true) //網(wǎng)絡(luò)請求完成并且是成功的
          observerA.sendCompleted()
      }

      requestA.observeValues { (success) in
          if success {
              self.networkRequest(sleepTime: 1, closure: {
                  print("網(wǎng)絡(luò)請求B完成")
              })
          }
      }
  }

順便說說柵欄函數(shù) dispatch_barrier_async

這是可以在并行隊(duì)列中插入一個操作,例如任務(wù)1234都是并行的,如果在代碼中12和34插入dispatch_barrier_async 則可以保證 dispatch_barrier_async 中的內(nèi)容在12后、34前執(zhí)行。

    func test() {
        let group = DispatchGroup.init()
        let queue = DispatchQueue.init(label: "xQ")
        
        queue.async(group: group) {
            print("1 start")
        }
        
        queue.async(group: group) {
            print("2 start")
        }
        
        queue.async(group: group, flags: .barrier) {
            print("5 start")
        }
        
        queue.async(group: group) {
            print("3 start")
        }
        
        queue.async(group: group) {
            print("4 start")
        }
    }

這里可以保證輸出為 12/21 5 34/43 這樣的順序,5肯定夾在中間。
不過注意這里有個十分坑的地方:對于 DispatchQueue.global() 這個函數(shù)不起效!
在 swift 文檔中并沒有提到,我是在看 OC 文檔中發(fā)現(xiàn)的解釋,對 DispatchQueue.global() 不起效,僅相當(dāng)于 dispatch_async,dispatch_barrier_async 只對自己創(chuàng)建的隊(duì)列才生效。

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.

最后說一句,swift3 中的 GCD 幾乎全部換新,具體使用必須看官方文件。

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

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

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