定时器作业是否适合这项工作?

时间:2013-03-28 22:26:33

标签: c# asp.net web-applications timer signalr

假设您有一个应用程序,允许用户监控他们所有的牛奶壶。所有牛奶壶都会在某个时候到期。

我想要监控所有的牛奶壶,看看他们的“isExpired”属性是真是假。

我是否应该偶尔使用timerjob来迭代所有的水壶以更新属性?如果我想在一个水壶过期后立即即时反馈怎么办?在.NET / C#世界中,我可以使用哪些工具来实现这种便宜/高效的工具?

一旦jug过期,我最终会使用signalR之类的东西将通知推送到网络客户端 - 我只是不知道如何判断一个水壶过期而不经常迭代所有东西。

==

原谅我把两个问题塞进去,但显然当用户正在观看水壶时发送通知是很重要的。那么也许只有在用户的会话处于活动状态时运行的timerjob才有效?这里的任何提示/推荐阅读都会很棒。

3 个答案:

答案 0 :(得分:4)

如果您希望在水壶过期时发出即时通知,您可以执行以下操作:

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源的链接。

评论后的其他信息

我怀疑,在你的评论之后,你想要通知登录用户过期的水壶。我可能会使用一个集合(列表或优先级队列),其中包含每个登录用户的一个jug:最早到期的用户。当用户的项目到期时,您可以执行数据库查询以获取该用户下一个将过期的数据库查询,并将其添加到列表中。当然,这取决于您希望拥有多少用户,每个人拥有多少个水壶,以及事情过期的频率。

你可以尝试将它们全部留在内存中,但是你必须管理更新。也就是说,如果数据库得到更新,您还必须更新内存中的表示。如果每个用户只保留一个,那么问题就会大大简化。而且它还需要更少的内存。

无论您决定如何填充列表,基本想法都保持不变:创建一个配置为在最早的项目到期时触发的计时器。当每个项目到期时,重置下一个项目的计时器。

您决定如何将项目添加到列表中只是“只是一个细节”。我无法提出更具体的建议,因为我对您的申请知之甚少。

答案 1 :(得分:1)

最简单的解决方案是使用Timer(http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx),然后在计时器到期时抛出一个事件。可以基于用户是否正在观看来设置/重置计时器。一旦在服务器上抛出事件,您就可以使用SignalR在客户端上调用相关方法/通知用户。

答案 2 :(得分:0)

使用sortedlist来保留你的牛奶,然后你只需要检查一个(或者更确切地检查直到失败,但通常是1)。然后你可以在刻度线上取剩余时间差,如果它低于默认值(30秒?),则缩短时间间隔。

另外一条忠告不会更新属性,但是有一个timedate属性expirationDate,要求“剩余秒数”或者你需要更新的任何东西都要求麻烦。 (抱歉,您对更新属性的评论让我很担心),至于实际过期的属性,只需让get操作检查过期日期,不用担心设置它。

好吧,它可以检查一个活跃的会话是。