为每个实例创建一个计时器有什么问题吗?

时间:2014-03-20 08:23:26

标签: c# events timer

为了更好地理解,我将以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;
        }
    }
}

创作时有什么需要考虑 - 例如 - "几千"这样一个类的实例?我最关心的是计时器。 我应该考虑这样一个任务的另一种设计模式(比如所有租约的经理,或者根本不使用计时器?)或者在创建大量计时器时没有什么可担心的,这是适当的方式吗?至少我总是试图在计时器和事件时保持谨慎。

4 个答案:

答案 0 :(得分:2)

您可以只存储每个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的{​​{1}} ExpirationDate属性,则可以通过在要停止时将运行设置为false来取消此属性

答案 1 :(得分:1)

根据System.Timers.Timer MSDN page

  

基于服务器的Timer设计用于a中的工作线程   多线程环境。服务器计时器可以在线程之间移动   处理凸起的Elapsed事件,导致更准确   Windows计时器按时提升事件。

这意味着当您同时运行几千个计时器时,不太可能导致问题。

这并不意味着这是一个好方法,你应该寻找一个更集中的解决方案来解决这个问题。

答案 2 :(得分:1)

我建议使用System.Threading.Timer代替System.Timers.Timer。第二个是关于第一个在设计时可见的包装器,如果你真的不需要设计时间支持则没有必要。 Timer内部调用ThreadPool.QueueUseWorkItem ,而线程池负责维护计时器tick上的线程。 线程池只使用一个线程来维护所有的定时器对象,并且该线程决定每个定时器队列在计时器上的新线程时刻录。

我不能看到任何的开销,除非你的计时器打得如此之快,以至于你无法在tick工作中完成所有操作而只是在线程池中排队太多工作。

答案 3 :(得分:1)

我认为这部分取决于您在服务器上可用的资源,以及您需要的准确性和性能。

另一种方法可能是在每个实例中存储像时间戳一样简单的东西,并定期检查该值,将其与当前时间进行比较,并适当地更新它。我预感到可能在性能上更容易 - 但你应该尝试以某种方式对它进行基准测试以确定。

当然,如果你有大量的实例,迭代所有实例可能也需要一些时间,所以可能将这些组合成组,其中每个组在常规(可调节?)间隔的单独线程中处理可能是一个选择。

如果没有关于性能的一些信息,在这里给出一个很好的答案有点困难,所以你应该只是创建一个概念证明,并测试你认为可能有用的几个策略,并尝试对它们进行基准测试看哪个最合适。