计时器作业是这项工作的正确工具吗?

本文关键字:工具 工作 作业 计时器 | 更新日期: 2023-09-27 18:06:18

假设您有一个应用程序,可以让用户监控他们所有的牛奶罐。所有的牛奶罐总有一天会过期。

我想监控所有的牛奶罐,看看他们的"isExpired"属性应该是真还是假。

我是否应该每隔一段时间就迭代所有的罐子,使用timerjob来更新属性?如果我想要即时反馈,一旦一个罐子过期?在。net/c#的世界里,有什么工具可以让我做到这一点?

我最终会使用像signalR这样的东西来推送通知到web客户端,一旦一个罐子过期了——我只是不知道如何确定一个罐子什么时候过期了,而不是不断地迭代所有的东西。

= =

请原谅我在这里塞了两个问题,但很明显,只有当用户正在观看罐子时,才需要发送通知。因此,也许只有在用户会话处于活动状态时才运行的计时器作业可以工作?任何提示/推荐的阅读都会很棒。

计时器作业是这项工作的正确工具吗?

如果您希望在容器过期时立即通知,您可以执行以下操作:

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

注释后的附加信息

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

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

无论您决定如何填充列表,基本思想都是相同的:创建一个定时器,配置为在最早的项到期时触发。当每个物品过期时,重置下一个物品的计时器。

如何决定将项目添加到列表中"只是一个细节"。我不能给你更具体的建议,因为我对你的申请了解得不够。

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

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

还有一个建议,不要更新属性,但是要有一个定时属性expirationDate,要求"剩余秒数"或任何你必须更新的东西都是自找麻烦。(抱歉你关于更新属性的评论让我担心),至于实际的是过期的属性,只是让get动作检查过期日期,不要担心设置它。

它可以检查是否有活动会话。