如果您想在水壶过期时立即收到通知,您可以执行以下操作:
Scan the list of jugs and find the one with the earliest expiration date
Set a timer to expire at that date/time.
When the timer expires, remove that jug from the list.
Scan the list of jugs to find any other jugs that expire at this time.
Send notifications for all the expired jugs.
Go to step 1.
这个算法有几个问题:
- 如果你添加一个水壶,你必须检查它的过期时间是否在当前最早的过期时间之前。如果是这样,您必须为当前水壶的到期时间重置计时器。
- 同样,如果您从列表中删除一个水壶,您必须检查它是否是您等待到期的那个。如果是这样,您必须找到新的最早的水壶并重置计时器。
- 扫描一个非常大的水壶列表可能会很昂贵,尽管你只需要在一个水壶过期时才这样做。
您可以通过对列表进行排序来免除自己扫描列表的麻烦。然后你知道最早的到期日期总是在列表的顶部。添加新水壶时,只需按过期时间插入即可。您可以使用二进制搜索来确定插入的位置。
或者,您可以使用优先级队列(二进制堆或跳过列表)按到期日期存储罐子。您使用哪种数据结构取决于您拥有多少项目以及更新它的频率。罐子越多,访问它们的频率越高,您就越应该关注有效的数据结构。
这种方法的美妙之处在于,您的计时器仅在您需要使水壶过期时才会滴答作响。你从不投票。
在 .NET 中创建这样的一次性计时器很简单:
TimeSpan expirationDelay = jug.ExpirationDateTime - DateTime.Now;
System.Threading.Timer jugTimer = new System.Threading.Timer(
JugExpirationCallback, null, expirationDelay, TimeSpan.FromMilliseconds(-1));
构造函数的最后一个参数告诉它不是周期性计时器,而是触发一次并退出。然后,您可以通过调用计时器的Change
方法来更新下一个水壶的计时器。
请注意,计算expirationDelay
不考虑夏令时。如果您想这样做,您需要DateTime
在进行计算之前将这些值转换为 UTC。或者,您可以使用WaitableTimer来执行此操作。文末有 WaitableTimer 源的链接。
评论后的附加信息
我怀疑,在您发表评论后,您想通知登录用户有关过期的水壶。我可能会使用一个集合(列表或优先级队列),其中包含每个登录用户的一个水罐:最早过期的一个。当用户的项目过期时,您可以执行数据库查询以获取该用户下一个将过期的项目,并将其添加到列表中。当然,这取决于您希望拥有多少用户、每个用户拥有多少罐子以及物品多久到期一次。
您可以尝试将它们全部保存在内存中,但是您必须管理更新。也就是说,如果数据库得到更新,您也必须更新内存中的表示。如果每个用户只保留一个,这个问题就大大简化了。而且它还需要更少的内存。
无论您决定如何填充列表,基本思想都保持不变:创建一个计时器,该计时器配置为在最早的项目到期时触发。当每个项目到期时,重置下一个项目的计时器。
您如何决定将项目添加到列表中“只是一个细节”。我无法提出更具体的建议,因为我对您的申请了解不够。