2016-07-15 2 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個の番号を印刷することができ、新しい非同期ジョブを実行していない、しかし、print文は低迷方法でものを出力します。私は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タイマーはタイマーキャンセル/終了します。これは美味しい解決策です、IMHO。

  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() 
    } 
    

    をこれが合体問題を解決するだけでなく、より効率的です。

については、previous version of this answerを参照してください。

関連する問題