A large number of timers

I need to write a component that receives an event (the event has a unique identifier). Each event requires me to send a request. The event indicates a waiting period waiting for a response from the request.

If the answer comes before the timer fires, fine, I cancel the timer. If the timer fires first, the timeout expires and I want to move on.

This waiting period is indicated in the event, so it is not constant. The expected waiting period is from 30 seconds to 5 minutes.

I see two ways to implement this.

  • Create a timer for each event and place it in the dictionary that associates the event with the timer.
  • Create an ordered list containing a TimeTime DateTime, and a new thread looping every 100 ms to check if the time is up.

Option 1 looks like the easiest solution, but I'm afraid that creating so many timers might not be a good idea, because timers may be too expensive. Are there any pitfalls when creating a large number of timers? I suspect that in the background, a timer implementation may actually be an effective implementation of option 2. If this option is a good idea, which timer should I use? System.Timers.Timer or System.Threading.Timer.

Option 2 seems to work more and may not be an effective solution compared to option 1.

Update

The maximum number of timers that I expect is in the range of 10,000, but more likely in the range of 100. In addition, disabling the timer before firing is normal.

Update 2

I conducted a test using 10K instances of System.Threading.Timer and System.Timers.Timer , monitoring the number of threads and memory. System.Threading.Timer seems to be “lighter” compared to System.Timers.Timer , judging by the memory usage, and there was no creation of an excessive number of threads for both timers (i.e., the thread pool is working fine). So I decided to continue and use System.Threading.Timer .

+7
c # timer
source share
4 answers

You should do this in the easiest way. If you are concerned about performance, you should run the application through the profiler and identify bottlenecks. You may be very surprised to learn that it was some kind of code that you least expected, and you optimized your code for no reason. I always write the simplest code since it is the simplest. See PrematureOptimization

I do not understand why there are some pitfalls with a lot of timers. Are we talking about ten, or 100, or 10000? If it is very high, you may have problems. You can write a quick test to test this.

As for which of these Timer classes to use: I don't want any of the thefts to answer, who probably did a lot more research: look at this answer to this question `

+2
source share

The first option simply will not scale, you will need to do something else if you have many parallel timeouts. (If you don't know how much you have enough to be a problem, feel free to use timers to see if you really have a problem.)

However, your second option will require some tweaking. Instead of having a tight loop in a new thread, just create one timer and set its interval (each time it fires) as the time interval between the current time and the “subsequent” timeout time.

+3
source share

I do this on embedded systems (pure c), where I cannot write a lot of resources (for example, 4k RAM is system memory). This is one approach that has been used (successfully):

  • Create a single system timer (interrupt) that turns off periodically (for example, every 10 ms).
  • A “timer” is an entry in the dynamic list that indicates how many “ticks” remain before the timer.
  • Each time the system timer goes off, iterate over the list and decrease each of the "timers". Everyone that is zero is "fired." Remove it from the list and do whatever the timer should have done.

What happens when the timer goes off depends on the application. This can be started by the state machine. This may be a function call. This may be an enumeration in which the execution code is indicated, what to do with the parameter that sent him the "Create a timer" call. The information in the timer structure is what is needed in the design context. "Number of ticks" is a secret sauce.

We also created this by returning an "identifier" for the timer (usually this is the address of the timer structure that is retrieved from the pool), so it can be canceled or the status on it can be obtained.

Convenience features convert seconds to ticks, so the timer creation API is always in terms of seconds or milliseconds.

You set the tick interval to a reasonable value to compromise detail.

I made other implementations of this in C ++, C #, objective-C, with a slight change in the general approach. This is a very general design / architecture of timer subsystems. You just need something to create a fundamental “tick”.

I even did it once with a tough "main" cycle and a stopwatch from a high-precision internal timer to create my own "simulated" tick when I did not have a timer. I do not recommend this approach; I simulated hardware in a direct console application and did not have access to system timers, so this was a bit of an extreme case.

Iterating over a list of hundreds of timers 10 times per second is not such a big deal on a modern processor. You can also overcome this by inserting items with "delta seconds" and placing them in a list in sorted order. Therefore, you should check only those listed at the top of the list. This will allow you to solve scaling problems, at least in terms of list repetition.

Was this helpful?

+3
source share

Let me suggest a different architecture: for each event, just create a new Task and send a request and wait 1 to answer there.

The ~ 1000 tasks should scale just fine, as shown in this early demo . I suspect that 10,000 tasks will still scale, but I have not tested this myself.


1 Consider fulfilling the wait by adding a continuation to Task.Delay (instead of just Thread.Sleep) to avoid under-subscription.

+1
source share

All Articles