5

假设您有一个应用程序可以让用户监控他们所有的牛奶罐。所有的牛奶罐都会在某个时候过期。

我想监控所有的牛奶罐,看看它们的“isExpired”属性是真还是假。

我是否应该每隔一段时间使用诸如 timerjob 之类的东西来迭代所有的水罐来更新属性?如果我想在水壶过期后立即获得反馈怎么办?在 .NET/C# 世界中,我可以使用哪些工具来降低成本/提高效率?

一旦水罐过期,我最终会使用诸如 signalR 之类的东西向 Web 客户端推送通知——我只是不知道如何确定水罐何时过期,而不需要不断地迭代所有内容。

==

请原谅我在其中塞入两个问题,但显然只有在用户观看水壶时发送通知才重要。因此,也许仅在用户会话处于活动状态时运行的 timerjob 会起作用?任何提示/推荐阅读这里都会很棒。

4

3 回答 3

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

评论后的附加信息

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

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

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

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

于 2013-03-28T23:38:16.703 回答
1

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

于 2013-03-28T23:16:02.500 回答
0

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

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

好吧,它可以检查活动会话是的。

于 2013-03-28T22:34:16.343 回答