为每个实例创建计时器有什么问题吗?
本文关键字:什么 问题 计时器 实例 创建 | 更新日期: 2023-09-27 17:51:00
为了更好地理解,我将以DHCP租期的简单抽象为例:租期包含IP地址和MAC地址,租期被授予的时间,并且可以在给定的时间范围内更新。一旦过期,将调用一个事件。同样,这只是我能想到的最简单的例子:
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Timers;
namespace Example
{
public class Lease
{
public IPAddress IP
{
get;
private set;
}
public PhysicalAddress MAC
{
get;
private set;
}
public DateTime Granted
{
get;
private set;
}
public event EventHandler Expired;
private readonly Timer timer;
public Lease(IPAddress ip, PhysicalAddress mac, TimeSpan available)
{
IP = ip;
MAC = mac;
timer = new Timer();
timer.AutoReset = false;
timer.Elapsed += timerElapsed;
Renew(available);
}
public void timerElapsed(object sender, EventArgs e)
{
var handle = Expired;
if (handle != null)
{
handle(this, EventArgs.Empty);
}
}
public void Renew(TimeSpan available)
{
Granted = DateTime.Now;
timer.Interval = available.TotalMilliseconds;
timer.Enabled = true;
}
}
}
在创建——例如——"几千个"这样一个类的实例时,有什么需要考虑的吗?我最担心的是计时器。对于这样的任务,我是否应该考虑另一种设计模式(比如所有租约的管理器,或者根本不使用计时器?)或者在创建大量计时器时没有什么可担心的,这是合适的方式?至少当涉及到计时器和事件时,我总是尽量保持谨慎。
无需创建数千个计时器,只需存储每个Lease
对象的过期时间,然后在单个线程中定期查询过期的对象。
一个我头脑中的代码示例:
var leases = new List<Lease>();
var running = true;
var expiredChecker = Task.Factory.StartNew(() =>
{
while (running)
{
var expired = leases.All(l => l.ExpirationDate < DateTime.Now);
// do something with the expired lease objects
}
});
假设你有一个IEnumerable<Lease>
,一个DateTime
属性称为ExpirationDate
在你的Lease
对象,然后你可以取消它通过设置运行为false当你想要停止。
我认为这部分取决于服务器上可用的资源,以及需要什么样的准确性和性能。
另一种方法可能是在每个实例中存储时间戳这样简单的东西,并定期检查该值,将其与当前时间进行比较,并适当地更新它。我有一种预感,这个可能在性能上更容易-但您应该尝试以某种方式对其进行基准测试以确保。
当然,如果你有大量的实例,遍历它们也可能需要一些时间,所以也许可以将它们池成组,其中每个组在一个单独的线程中定期(可调?)间隔处理可能是一个选项。
如果没有一些关于性能的信息,在这里很难给出一个很好的答案,所以你可能应该只是创建一个概念证明,并测试一些你认为可能有效的策略,并尝试对它们进行基准测试,看看哪一个最适合。
根据System.Timers.Timer
MSDN页面:
基于服务器的Timer被设计为与工作线程一起使用多线程环境。服务器计时器可以在线程之间移动到处理引发的Elapsed事件,从而获得比Windows计时器在触发事件的时间。
这意味着当您同时运行数千个计时器时,它不太可能引起问题。
这并不意味着这是一个好方法,您可能应该寻找一个更集中的解决方案来解决这个问题。
我建议使用System.Threading.Timer
而不是System.Timers.Timer
。第二个是关于第一个在设计时可见的包装,如果你真的不需要设计时支持,它是不必要的。Timer内部调用ThreadPool。QueueUseWorkItem,那么线程池负责维护线程的计时器tick。线程池只使用一个线程来维护所有的计时器对象,该线程决定每个计时器队列上的新线程何时在计时器上滴答。
我不能看到任何开销,除非你的计时器将滴答如此之快,你不能做所有的滴答工作,你只是在线程池中排队太多的工作。