dispatch_get_main_queue() 不能顺利 运行 新的异步作业
dispatch_get_main_queue() doesn't run new async jobs smoothly
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()
不能很好地处理更多的异步任务。
提前致谢!
问题不在于 dispatch_get_main_queue()
。 (如果您使用不同的队列,您会注意到相同的行为。)问题出在 dispatch_after()
.
当您使用 dispatch_after
时,它会创建一个具有 start
/when
的 10% 回旋余地的调度计时器。见苹果githublibdispatch 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 计时器在定时器是 canceled/finishes。这是不优雅的解决方案,恕我直言。
仅安排每 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。
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()
不能很好地处理更多的异步任务。
提前致谢!
问题不在于 dispatch_get_main_queue()
。 (如果您使用不同的队列,您会注意到相同的行为。)问题出在 dispatch_after()
.
当您使用 dispatch_after
时,它会创建一个具有 start
/when
的 10% 回旋余地的调度计时器。见苹果githublibdispatch 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 计时器在定时器是 canceled/finishes。这是不优雅的解决方案,恕我直言。仅安排每 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。