假设您有一个应用程序,允许用户监视他们的所有牛奶壶.所有牛奶壶都会在某个时候到期.
我想要监控所有的牛奶壶,看看他们的“isExpired”属性是真是假.
我应该每隔一段时间用一些像timerjob这样的东西迭代所有的水罐来更新属性吗?如果我想要一个水壶过期后立即反馈怎么办?在.NET / C#世界中,我可以使用哪些工具来实现这种便宜/高效的工具?
一旦jug过期,我最终会使用signalR之类的东西将通知推送到网络客户端 – 我只是不知道如何确定一个水壶过期而不经常迭代所有东西.
==
原谅我把两个问题塞进去,但显然当用户正在观看水壶时发送通知是很重要的.那么也许只有在用户的会话处于活动状态时运行的timerjob才有效?这里的任何提示/推荐阅读都会很棒.
解决方法:
如果您想在水壶过期时立即通知,您可以执行以下操作:
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方法更新下一个jug的计时器.
请注意,expirationDelay的计算不考虑夏令时.如果要这样做,您需要在进行计算之前将DateTime值转换为UTC.或者,您可以使用WaitableTimer来执行此操作.本文末尾有一个WaitableTimer源代码链接.
评论后的其他信息
在您的评论之后,我怀疑您想要通知登录用户过期的水壶.我可能会使用一个集合(列表或优先级队列),其中包含每个登录用户的一个jug:最早到期的用户.当用户的项目到期时,您可以执行数据库查询以获取该用户下一个将过期的数据库查询,并将其添加到列表中.当然,这取决于您期望拥有多少用户,每个用户有多少个水壶,以及事情过期的频率.
您可以尝试将它们全部保留在内存中,但是您必须管理更新.也就是说,如果数据库得到更新,您还必须更新内存中的表示.如果每个用户只保留一个,那么问题就会大大简化.而且它还需要更少的内存.
无论您如何决定填充列表,基本思路都保持不变:创建一个配置为在最早的项目到期时触发的计时器.当每个项目到期时,重置下一个项目的计时器.
您如何决定将项目添加到列表中只是“一个细节”.我无法提出更具体的建议,因为我对您的申请知之甚少.