swift2 dispatch_get_main_queue()无法顺利执行新的异步工作

jecbmhm3  于 2022-11-06  发布在  Swift
关注(0)|答案(1)|浏览(194)
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变大时,print语句打印的东西很慢。我在dispatch_after内部有更复杂的逻辑,我注意到处理非常慢,这就是我写这个测试的原因。
是否有缓冲区大小或其他属性可以配置?似乎dispatch_get_main_queue()不能很好地处理大量的异步任务。
提前感谢!

ohtdti5x

ohtdti5x1#

问题不在于dispatch_get_main_queue()。(如果使用不同的队列,您将注意到相同的行为。)问题在于dispatch_after()
当你使用dispatch_after时,它会创建一个调度计时器,其误差为start/when的10%。(start ± 10%leeway)重叠时,它可能会开始合并它们。当它们合并时,它们会看起来像是以“聚集”的方式发射,一群人紧接着另一群人开火,然后在到达下一群人之前有一点延迟。
有两种解决方案,所有这些解决方案都需要淘汰dispatch_after调用系列:
1.您可以手动构建计时器,强制DispatchSource.TimerFlag.strict禁用合并:

let group = DispatchGroup()
 let queue = DispatchQueue.main

 let start = CACurrentMediaTime()

 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.schedule(deadline: .now() + Double(i) * 0.1)
     timer.resume()
 }

 group.notify(queue: .main) {
     let elapsed = CACurrentMediaTime() - start
     os_log("all done %.1f", elapsed)
 }

就我个人而言,我不喜欢闭包中对timer的引用,但是您需要保留一些对它的强引用,直到计时器触发,并且当计时器取消/完成时,GCD计时器释放块(避免强引用循环)。恕我直言,这是一个不优雅的解决方案。
1.更有效的方法是仅调度每0.1秒触发一次的单个重复计时器:

var timer: DispatchSourceTimer?    // note this is property to make sure we keep strong reference 

 func startTimer() {
     let queue = DispatchQueue.main

     let start = CACurrentMediaTime()

     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 = CACurrentMediaTime() - start
             os_log("all done %.1f", elapsed)
             return
         }
         os_log("%d", counter)
         counter += 1
     }
     timer!.schedule(deadline: .now(), repeating: 0.05)
     timer!.resume()
 }

这不仅解决了聚结问题,而且效率更高。
有关Swift 2.3格式副本,请参阅previous version of this answer

相关问题