【问题标题】:Executing a function at several timestamps在多个时间戳执行函数
【发布时间】:2019-03-27 03:43:19
【问题描述】:

我正在尝试实现(在 C++11/14 中)一种在多个时间戳执行函数的方法。场景如下:

有一个主线程可以随时接收请求。请求可以是以下格式之一:remove object X at time Yinsert object X at time Y,其中 X 是我用来标识存储中的对象的 id,Y 是 Unix 时间戳。时间分辨率将排在第二位。

一种直观的方法是在新请求到来时创建一个新线程。然后,这个新线程会休眠请求中指定的一段时间,并在唤醒时移除/添加一个对象。

另一种方式是轮询。主线程可以存储收到的请求。然后一个单独的线程可以每秒检查所有收到的请求,看看是否需要进行任何删除/插入。

我提到的两种方法似乎都会产生巨大的开销,所以我正在寻找另一种方法。

【问题讨论】:

  • 你怎么知道开销很大?你能在这里分享一些代码吗?
  • 您使用的时间分辨率是多少?秒、毫秒还是微秒(或其他)?
  • 有一个增量列表,其中包含“执行列表中第一项的时间”。警报处理代码处理唤醒并执行适当的操作。当一个新事件到达时,如果它应该在下一个事件发生之后发生,只需将其插入到增量列表中的正确位置即可。当新事件应该在列表中的当前第一项之前发生时,将其插入列表并唤醒执行线程,让它知道有一个新的“列表中的第一个”项要执行。
  • 一个增量列表意味着如果第一个项目将在 10 秒内执行,下一个在 15 秒内执行,第三个在 18 秒内执行,您的增量列表将记录 'in 10 seconds', '再过 5 秒' 和 '再过 3 秒' 。您将记录足够的时间信息,以便该进程可以在提早唤醒时重新同步。

标签: c++ multithreading asynchronous async-await


【解决方案1】:

您要做的是实现一个简单的任务调度程序。

最简单、最有效的方法只需要一个线程、一个当前时间调用(例如std::chrono::system_clock::now())以及加减时间戳的数学运算:

  1. 您需要一个数据结构来充当您的日程安排。您的程序将使用此数据结构来跟踪何时需要执行哪些操作。它将保存的数据项每个都应该包含一个时间戳,以及执行当时要完成的操作所需的任何信息/参数。大多数数据结构都可以用于此任务,但std::priority_queue 通常是最有效的选择。

  2. 对于您希望在特定时间执行的每个操作,将该操作的记录插入到您的数据结构中(连同您希望它执行操作的时间戳——例如,如果您想要它从现在开始 10 秒后完成,将 10 秒添加到当前系统时间并将其用作记录的时间戳)。

  3. 在您的计划中找到最小的时间戳,并从中减去当前系统时间。如果日程表上根本没有事件,请选择一个任意但很长的时间,例如1小时或1周等;或者如果您的计算值是负数,请改用 0。

  4. 按照您在步骤 3 中计算的时间量进行睡眠。

  5. 从睡梦中醒来后,请检查日程中最早事件的时间戳。如果它小于或等于当前系统时间,则执行该事件,从调度中删除该事件,然后转到 (5)。否则,转到 (2)。

这应该会以良好的准确性和最小的开销为您提供所需的行为。唯一的问题是,如果另一个线程想要在你的调度线程运行时调度另一个任务,它需要将你的调度线程从睡眠中唤醒,以便调度线程可以立即再次循环执行步骤 2-5(只需如果新插入的事件需要在它之前计划执行的事件之前发生)。这可以实现,例如通过使用std::condition_variablewait_for()wait_until() 作为您的睡眠机制,并让另一个线程在需要您早起时向条件变量发出信号。 (标准的多线程注意事项也适用,因此请务必使用互斥锁或临界区序列化对调度数据结构的访问)

这样做的好处是你的线程几乎永远不会唤醒,除非它有事情要做;例如,如果您每小时只收到一两个请求,那么您的线程每小时只会唤醒一到两次。此外,计时的准确性可以根据需要进行细粒度或粗粒度;例如如果你想要 1 秒的粒度,你可以使用以秒为单位计算的时间戳,或者如果你想要更精细的精度,你可以使用微秒或纳秒等;无论哪种方式,逻辑都是相同的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多