我正在尝试实现(在C ++ 11/14中)在几个时间戳上执行函数的方法。情况如下:
有一个主线程可以随时接收请求。请求可以采用以下格式之一:remove object X at time Y
或insert object X at time Y
,其中X是我用来标识存储中对象的ID,Y是Unix时间戳。时间分辨率将是秒。
每当有新请求出现时,一种直观的方式就是创建一个新线程。然后,这个新线程休眠请求中指定的一段时间,并在唤醒时删除/添加对象。
另一种方式是轮询。主线程可以存储收到的请求。然后,一个单独的线程可以每秒检查所有收到的请求,并查看是否需要进行删除/插入。
似乎我提到的两种方法都可能产生巨大的开销,因此我正在寻找一种替代方法。
答案 0 :(得分:1)
您要执行的是实现一个简单的任务计划程序。
最简单,最有效的方法仅需一个线程,当前时间是什么时间(例如std::chrono::system_clock::now())以及用于添加和减去时间戳记的数学运算即可:
您将需要一个数据结构作为您的日程表。您的程序将使用此数据结构来跟踪何时需要执行哪些操作。它将保存的数据项都应包含一个时间戳,以及执行该次操作所需的任何信息/参数。大多数数据结构都可以用于此任务,但是std::priority_queue通常是最有效的选择。
对于要在特定时间执行的每个操作,请将该操作的记录插入数据结构中(以及您希望它在执行操作的时间戳记,例如,如果您愿意从现在起10秒钟完成,将10秒钟添加到当前系统时间并将其用作记录的时间戳。)
找到时间表中的最小时间戳,并从中减去当前系统时间。如果时间表上没有任何活动,请选择任意但很长的时间,例如1小时或1周等;或者,如果您的计算值是负数,请改用0。
睡眠一段时间(在步骤3中计算出的时间)。
一旦您从睡眠中醒来,请检查时间表中最早事件的时间戳。如果它小于或等于当前系统时间,则执行该事件,从调度中删除该事件,然后转到(5)。否则,转到(2)。
这应该可以使您以良好的准确性和最小的开销获得所需的行为。唯一的麻烦是,如果另一个线程想在您的调度程序线程运行时调度另一个任务,它将需要将您的调度程序线程从睡眠状态中唤醒,以便调度程序线程可以立即再次通过步骤2-5循环(仅万一新插入的事件需要在之前计划执行的事件发生之前 发生。可以例如实现通过使用std::condition_variable和wait_for()或wait_until()作为您的睡眠机制,并让另一个线程在条件变量需要您提前唤醒时发出信号通知状态变量。 (此外,标准多线程警告也适用,因此请确保使用互斥或关键部分来序列化对计划数据结构的访问)
以这种方式进行操作的好处是,除非有需要执行的操作,否则线程几乎不会唤醒。例如,如果您一个小时仅收到一两个请求,那么您的线程每小时只会被唤醒一两次。同样,定时的精度可以根据需要任意细化或粗化。例如如果您想要1秒钟的粒度,则可以使用以秒为单位计算的时间戳,或者,如果您想要更高的精度,则可以使用微秒或纳秒等。两种方法的逻辑都是相同的。