2016-07-15 30 views
1
let dispatchGroup = dispatch_group_create() 
let now = DISPATCH_TIME_NOW 

for i in 0..<1000 { 
    dispatch_group_enter(dispatchGroup) 

    // Do some async tasks 
    let delay = dispatch_time(now, Int64(Double(i) * 0.1 * Double(NSEC_PER_SEC))) 

    dispatch_after(delay, dispatch_get_main_queue(), { 
     print(i) 
     dispatch_group_leave(dispatchGroup) 
    }) 
} 

print語句可以順利地打印第15-20號,但是,當i去越大,打印語句打印的東西以一種緩慢的方式。我在dispatch_after裏面有更復雜的邏輯,我注意到處理非常緩慢,這就是我編寫這個測試的原因。dispatch_get_main_queue()不執行新的異步工作順利

是否有可以配置的緩衝區大小或其他屬性?看起來dispatch_get_main_queue()不適用於更多數量的異步任務。

在此先感謝!

回答

2

問題不是dispatch_get_main_queue()。 (如果你使用不同的隊列,你會注意到相同的行爲。)問題在於dispatch_after()

當您使用dispatch_after時,它會創建一個派遣計時器,其回報率爲start/when的10%。請參閱Apple github libdispatch source。最終結果是,當這些定時器(start±10%leeway)重疊時,它可能會開始合併它們。當他們融合在一起時,他們似乎會以一種「團塊」的方式開火,一羣人立即接連發射,然後稍微延遲一會兒,然後到達下一組。

有一對夫婦的解決方案,都將會導致一系列的dispatch_after呼叫退休:

  1. 您可以手動構建計時器,迫使DispatchSource.TimerFlag.strict禁用凝聚:

    let group = DispatchGroup() 
    let queue = DispatchQueue.main 
    
    let start = CFAbsoluteTimeGetCurrent() 
    
    os_log("start") 
    
    for i in 0 ..< 1000 { 
        group.enter() 
    
        let timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue) // use `.strict` to avoid coalescing 
        timer.setEventHandler { 
         timer.cancel()  // reference timer so it has strong reference until the handler is called 
         os_log("%d", i) 
         group.leave() 
        } 
        timer.scheduleOneshot(deadline: .now() + Double(i) * 0.1) 
        timer.resume() 
    } 
    
    group.notify(queue: .main) { 
        let elapsed = CFAbsoluteTimeGetCurrent() - start 
        os_log("all done %.1f", elapsed) 
    } 
    

    就個人而言,我不喜歡在閉包內部引用timer,但是你需要保留一些強引用,直到定時器觸發,並且GCD定時器在定時器釋放塊(避免強引用週期)時被取消/結束。這是不雅的解決方案,恕我直言。

  2. 它是更有效的調度只是單一重複計時器每0.1秒觸發:

    var timer: DispatchSourceTimer? // note this is property to make sure we keep strong reference 
    
    func startTimer() { 
        let queue = DispatchQueue.main 
    
        let start = CFAbsoluteTimeGetCurrent() 
    
        var counter = 0 
    
        // Do some async tasks 
    
        timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue) 
        timer!.setEventHandler { [weak self] in 
         guard counter < 1000 else { 
          self?.timer?.cancel() 
          self?.timer = nil 
          let elapsed = CFAbsoluteTimeGetCurrent() - start 
          os_log("all done %.1f", elapsed) 
          return 
         } 
         os_log("%d", counter) 
         counter += 1 
        } 
        timer!.scheduleRepeating(deadline: .now(), interval: 0.05) 
        timer!.resume() 
    } 
    

    這不僅解決了聚結的問題,但它也更有效。

對於Swift 2.3的再現,請參閱previous version of this answer