简体   繁体   中英

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. 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.

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.

According to the System.Timers.Timer MSDN page :

The server-based Timer is designed for use with worker threads in a multithreaded environment. 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.

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 . 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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