如何有效地实现大量计时器

How to implement a large number of timers efficiently?

本文关键字:计时器 实现 有效地      更新时间:2023-10-16

我正在用c++编写一个程序,该程序可能有数十万个对象,每个对象都有过期时间,也就是说,如果它们在一定时间内不活动,则应该删除它们。许多对象非常频繁地活动,新对象也非常迅速地创建。

我一直在犹豫该用什么方法。我一直在阅读有关亚洲计时器的文章,但我不太确定它们是否能很好地满足我的需求,如果能,又是如何满足的。我想到了两种方法:

  1. 我可以为每个对象创建一个死线计时器,并在对象执行某些操作时重置它。这意味着我将有成千上万个计时器。

  2. 我可以创建多个计时器队列,例如,30个计时器用于30秒的过期时间。第一个计时器用于剩余30秒生命周期的对象。如果它们在一秒钟内处于非活动状态,则将它们移动到对象的计时器队列中,并保留29秒的生命周期,依此类推。如果最后一个队列中的对象处于非活动状态一秒钟,则删除该对象。

第二种方法听起来更有效,但这是真的吗?如果Asio可以有效地处理大量计时器,那么第二种方法可能会浪费我的大量精力。我该怎么做呢?还是有更好的办法?

EDIT:我想我必须加上效率,我指的是更好的CPU利用率。在这里,内存使用是次要问题。

我将创建一个队列,根据要过期的剩余时间对对象进行排序。如果预期过期时间频繁,我会使用链表,否则使用数组,因为在列表中查找内容更快(但如果在对象本身而不是节点中保留对下一个和上一个对象的引用,则没关系)。

  • 现在你只需要一个定时器的对象与最低过期时间。
  • 当该定时器用完时,删除队列开头的对象,并为下一个对象准备定时器。

制作数字列表并给出定时器的逻辑。这是实现for timer最有效的方法,因为它消耗的内存更少。

我将采用第一种方法,并增加了一个变化,即只有当您尝试执行操作且截止日期已过时,您才会删除对象。它更容易实现,因为它是懒惰的,在某种意义上,你不计算计时器,直到你不得不(意思是当你试图使用对象)。

如果考虑到效率,无论何时删除对象,所做的工作都应该是相同的。

保存截止日期时间的内存将在每个截止日期8字节左右,这相当于64位系统上指针的大小(方法2所需)。

方法1的唯一风险是,如果你不经常使用它们,你可能会一次积累更多的对象。您可以通过定期扫描和删除对象来缓解这种情况。只要对象积累得不是太快,就可以相对不频繁地进行扫描。

如果不断地创建新对象并删除旧对象,那么考虑使用对象池,在程序启动时创建一堆对象。如果用完,动态增加对象的数量。按一定比例(10% - 25%)增加物体的数量,而不是一次增加一个。如果不再需要某个对象,则将其返回到对象池。如果需要考虑内存,请考虑使用一种算法来删除对象池中的对象。例如,它可以每隔5或10分钟删除75%不使用的对象。

要实现这一点,请考虑使用可用对象池列表和使用中列表。您的对象应该有一个初始化方法,可以调用该方法初始化现有对象。调用参数应该与构造函数非常相似。至于容器本身,您可能想尝试同时使用向量和链表,看看哪个更快。如果你的应用需要搜索对象,那么std::map或std::unordered_map将是更好的选择。当计时器唤醒时,可以使用迭代器遍历整个正在使用的列表。

至于计时器,你认为哪个会更快——让500,000个计时器唤醒并每秒做一次事情,还是让一个计时器每秒唤醒一次以遍历500,000个对象的列表以查看是否应该将对象返回到对象池?我认为很明显,使用一个计时器更有效率。我也很担心这50万个计时器现在或将来是否会在Windows上运行。我知道一些旧的windows操作系统肯定会崩溃。计时器应该多久唤醒一次?尝试使用1到5秒之间的值。这将是内存消耗与更长的唤醒时间之间的权衡。为更短的唤醒时间进行CPU节流。

你的第二种方法听起来像一些垃圾收集器所做的。微软的。net垃圾收集器被组织成几代,寿命短的对象进入第0代,寿命长的对象进入第1代,而寿命最长的对象进入第2代。

如果使用多线程,那么考虑每个线程使用一个对象池。每个对象池都有自己的计时器