【问题标题】:Difference between DispatchSourceTimer, Timer and asyncAfter?DispatchSourceTimer、Timer 和 asyncAfter 的区别?
【发布时间】:2019-08-03 12:27:01
【问题描述】:

我很难理解DispatchSourceTimerTimerasyncAfter 之间的主要区别(在我的情况下,用于安排需要每 X 秒运行一次的任务,尽管了解计时器的区别可以对有用)(或者除了列出的计时器之外,Swift 中还有另一种(更有效的)调度机制吗?)。

A Timer 需要在它启动的当前队列上有一个活动的运行循环。 DispatchSourceTimer 不需要那个。 Timer 使 CPU 不会进入空闲状态。这是否也适用于DispatchSourceTimer/asyncAfter

在什么情况下TimerDispatchSourceTimer/asyncAfter 更受欢迎?当然还有它们之间的区别?

我想在我的应用程序中每 15 秒在一个私有队列上安排一次工作。这意味着我必须使用DispatchSourceTimer,因为我在一个不是主线程的队列上(或者在队列中添加一个运行循环并使用Timer)。但是,我什至一开始就没有看到使用Timer 的任何好处。也许还有另一个操作,我可以在私有队列上每隔 X 秒使用一次调度工作,它比 DispatchSourceTimer 更有效,但我没有找到更好的解决方案。

DispatchSourceTimer 是否比 Timer 更有效?还是我应该使用asyncAfter 进行自调用方法?

这是创建计时器的代码。

asyncAfter

DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(2)) {
    // Code
}

定时器

Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
    // Code
}

DispatchSourceTimer

let timer = DispatchSource.makeTimerSource()

timer.schedule(deadline: .now() + .seconds(1))

timer.setEventHandler {
    // Code
}

timer.activate()

所有计时器的优缺点是什么?我什么时候应该使用一个在另一个之上?哪种计时器方式最有效?我想出了以下几点:

定时器

优点:

  • 可以失效
  • 无需参考
  • 可以在计划时停止。

缺点:

  • 防止 CPU 进入空闲状态
  • 需要在带有运行循环的队列上运行(否则什么都不会发生,甚至没有断言触发器...)

DispatchSourceTimer

优点:

  • 可以取消
  • 无需运行循环

缺点:

  • 需要强引用,否则会立即解除分配

asyncAfter

优点: - 无需运行循环

缺点: - 不能取消(我认为)

还有更多的计时器吗?为什么会有这么多定时器?我预计所有不同的计时器都会有一些真正的差异,但我找不到它们。

这里有很多问题,您可以阅读。主要问题是:哪些定时器可用,在什么情况下我应该使用哪些定时器,为什么?

【问题讨论】:

  • 试试这个链接medium.com/@danielemargutti/…,其中的差异得到了很好的解释。
  • @user3441734 这是一篇有趣的文章,但我仍然不明白什么时候该使用一个在另一个之上。

标签: swift timer grand-central-dispatch


【解决方案1】:

Timer 是 NSTimer 的 Swift 桥,它可以追溯到 NeXTSTEP,很久很久以前 Grand Central Dispatch (GCD) 和 DispatchSourceTimer 之类的东西直到 10.6 才出现(以 dispatch_source_set_timer 的形式)和 dispatchAfter (以 dispatch_after 的形式)。

NSTimer 基于运行循环,这是在 GCD 之前完成并发的主要方式。它是一个协作并发系统,主要设计为在单核上的单线程上运行(尽管它可以扩展到多线程环境)。

虽然运行循环在 Cocoa 中仍然非常重要,但它不再是管理并发的主要甚至首选方式。自 10.6 以来,GCD 一直是越来越受欢迎的方法(尽管在 10.12 时间范围内添加基于块的 NSTimer API 是一种受欢迎的现代化)。

在 15 秒的范围内,效率差异是无关紧要的。也就是说,我不明白您的评论“定时器使 CPU 不会进入空闲状态”。我不相信那是真的。在等待 NSTimer 触发时,CPU 肯定仍会进入空闲状态。

我不会为了运行 NSTimer 而设置运行循环。您最好将其安排在主运行循环上,然后使用DispatchQueue.async 在其他队列上执行实际工作。

一般来说,我使用满足需求的最高级别工具。随着时间的推移,Apple 可能会优化这些内容,而我所做的更改最少。例如,NSTimer 火灾日期会自动调整以提高能源效率。使用 DispatchSourceTimer,您可以控制 leeway 设置以获得相同的好处,但由您来设置它(默认为零,这具有最严重的能量影响)。当然,反过来也是如此。 DispatchSourceTimer 是最低级别,为您提供最大的控制权,因此如果您需要,那就使用它。

对于您的示例,我个人可能会使用 Timer 并将其作为块的一部分分派到私有队列。但是 DispatchSourceTimer 是完全合适的。

asyncAfter 确实是另一回事,因为它总是一次性的。如果您想要一次拍摄,那就太好了,但是如果您想重复,它会改变事情。如果你只是在块中调用 asyncAfter 来重复,它将在你最后一次完成之后 15 秒,而不是相隔 15 秒。随着时间的推移,前者往往会漂移得有点晚。设计问题是这样的:如果由于某种原因您的任务需要 5 秒才能完成,您是否希望下一个火灾事件在其结束后 15 秒内发生,或者您是否希望每个火灾事件之间保持恒定的 15 秒?您在那里的选择将决定哪个工具是正确的。

这里需要注意一点,NSTimer 事件总是比预定时间晚一点。有余地设置的 GCD 事件可能会有点早或有点晚。实际上,没有“准时”这样的事情(这是一个零长度的时期;你不会打它)。所以问题始终是你是否承诺会像 NSTimer 一样迟到,或者你可能会像 GCD 一样早到有余地。

【讨论】:

  • 那么,如果 NSTimer 使用的是运行循环,但后来被 GCD 淘汰了,那么重复调用代码块的首选高级方法是什么? DispatchSourceTimer 是否有更高级别的计数器部分?
  • @EricWang 运行循环并没有被 GCD 淘汰,NSTimer 也没有。如果您在 ObjC 中工作,NSTimer 完全适合它一直适用的用例。在 Swift 中,计时器。虽然运行循环不是管理并发的首选方式,但这并不意味着它们已经消失,并且与 (NS)Timer 关系不大,后者仍然是一个非常好的工具。
  • 至于重复asyncAfter,您可以轻松避免漂移:使用asyncAfter 作为 中的第一个操作安排下一次迭代,而不是最后一个。 OTOH,使用asyncAfter,不保证该块被按时调用; DispatchQueue 中此 func 的注释为:在指定时间后将工作项提交到调度队列以异步执行。如果您尝试使用此方法,您可以看到您的块执行的实际时间可能比 deadline 晚 10%。
猜你喜欢
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 2014-06-07
  • 2020-07-16
  • 1970-01-01
  • 2013-08-07
  • 2011-10-20
  • 2020-01-23
相关资源
最近更新 更多