简体   繁体   English

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

[英]Is there anything wrong with creating a timer for each instance?

For good understanding I will take a simple abstraction of DHCP lease as example: The lease contains the IP and MAC address, the time it was granted at and can be renewed with a given time span. 为了更好地理解,我将以DHCP租约的简单抽象为例:租约包含IP和MAC地址,它被授予的时间,并且可以在给定的时间跨度内更新。 Once expired an event will be invoked. 一旦过期,将调用一个事件。 Again, this is just serving as the most minimal example I could come up with: 同样,这只是我能想到的最小的例子:

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;
        }
    }
}

Is there anything to consider when creating - for example - "a few thousand" instances of such a class? 创建时有什么需要考虑的 - 例如 - 这类课程的“几千个”实例吗? I am mostly concerned about the timers. 我最关心的是计时器。 Should I consider another design pattern for such a task (like a manager for all the leases,or not use timers at all?) or is there nothing to worry about when creating a lot of timers, and this is the appropriate way? 我是否应该为这样的任务考虑另一种设计模式 (比如所有租约的经理,或者根本不使用计时器?) 或者在创建大量计时器时没有什么可担心的,这是合适的方法吗? At least I always try to be cautious when it comes to timers and events. 至少我总是试图在计时器和事件时保持谨慎。

Rather than creating thousands of timers, you could just store the expiration time of each Lease object, then in a single thread query for the expired ones periodically. 您可以只存储每个Lease对象的到期时间,而不是创建数千个计时器,然后定期在单个线程查询中查找已过期的对象。

An off the top of my head code example: 在我的头部代码示例的顶部:

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
    }
});

Assuming you have an IEnumerable<Lease> , a DateTime property called ExpirationDate on your Lease object, you can then cancel this by setting running to false when you want to stop. 假设您的Lease对象上有一个IEnumerable<Lease> ,一个名为ExpirationDateDateTime属性,则可以通过在要停止时将运行设置为false来取消它。

According to the System.Timers.Timer MSDN page : 根据System.Timers.Timer MSDN页面

The server-based Timer is designed for use with worker threads in a multithreaded environment. 基于服务器的Timer设计用于多线程环境中的工作线程。 Server timers can move among threads to handle the raised Elapsed event, resulting in more accuracy than Windows timers in raising the event on time. 服务器计时器可以在线程之间移动以处理引发的Elapsed事件,从而在准时引发事件时比Windows计时器更准确。

Which means it is not very likely to be causing issues when you are running a couple thousand timers at the same time. 这意味着当您同时运行几千个计时器时,它不太可能导致问题。

That doesn't mean it is a good approach, you should probably be looking for a more centralized solution to this problem. 这并不意味着它是一个好方法,你应该寻找一个更集中的解决方案来解决这个问题。

I recommend use a System.Threading.Timer instead of the System.Timers.Timer . 我建议使用System.Threading.Timer而不是System.Timers.Timer The second one is wrapper about the first one to be visible in the design time and it is not necessary if you really don't need design time support. 第二个是关于第一个在设计时可见的包装器,如果你真的不需要设计时间支持则没有必要。 Timer internally calls ThreadPool.QueueUseWorkItem , than threadpool is responsible for maintaining thread on timer tick. Timer内部调用ThreadPool.QueueUseWorkItem ,而线程池负责维护计时器tick上的线程。 Thread pool uses only one thread to maintain all the timers object and this thread decide when each timer queue new thread on timer tick. 线程池只使用一个线程来维护所有的定时器对象,并且该线程决定每个定时器队列中的新线程何时打开。

Than I cant see any overhead unless your timers will tick so quick than you are not able do all on tick job and you simply queue too much work in thread pool. 我不能看到任何开销,除非你的计时器打得如此之快,以至于你无法完成所有的工作,而你只是在线程池中排队太多工作。

I would suppose this depends partly on what resources you have available on your server, and what kind of accuracy and performance you need. 我认为这部分取决于您在服务器上可用的资源,以及您需要的准确性和性能。

An alternative approach might be to store something as simple as a time stamp in each instance, and checking that value regularly, comparing it to current time, and updating it appropriately. 另一种方法可能是在每个实例中存储像时间戳一样简单的东西,并定期检查该值,将其与当前时间进行比较,并适当地更新它。 I have a hunch that this might be easier on performance - but you should try to benchmark it somehow to be sure. 我有预感,这可能会更容易在性能上 - 但你应该尝试以某种方式对它进行基准测试以确保。

Of course, if you have a large number of instances, iterating over all of them might also take some time, so perhaps pooling these into groups, where each group is handled in a separate thread on regular (adjustable?) intervals might be an option. 当然,如果你有大量的实例,迭代所有实例也可能需要一些时间,所以可能将这些组合成组,其中每个组在常规(可调节?)间隔的单独线程中处理可能是一个选项。

It's a bit hard to give a great answer here without some info about performance, so you should probably just create a proof of concept, and test a couple of strategies that you think might work, and try to benchmark them to see which fits best. 如果没有关于性能的一些信息,在这里给出一个很好的答案有点困难,所以你应该只是创建一个概念证明,并测试你认为可能有效的几个策略,并尝试对它们进行基准测试,看看哪个最合适。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM