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()
不适用于更多的异步任务。
提前致谢!
答案 0 :(得分:2)
问题不在于dispatch_get_main_queue()
。 (如果您使用不同的队列,您会注意到相同的行为。)问题出在dispatch_after()
。
当您使用dispatch_after
时,它会创建一个调度计时器,其余地为start
/ when
的10%。请参阅Apple github libdispatch source。实际效果是,当这些计时器(start
±10%leeway
)重叠时,它可能会开始合并它们。当它们合并时,它们似乎会以“丛生”的方式发射,其中一堆它们会立即发射,然后在它到达下一束之前稍微延迟。
有几个解决方案,都需要退出dispatch_after
系列电话:
您可以手动构建计时器,强制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定时器释放块(避免强引用周期),当定时器是取消/完成。这是一个不优雅的解决方案,恕我直言。
仅安排每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。