0

I am developing a Windows Service application, in .NET, which executes many functions (it is a WCF service host), and one of the targets is running scheduled tasks.

I chose to create a System.Threading.Timer for every operation, with a dueTime set to the next execution and no period to avoid reentrancy.
Every time the operation ends, it changes the dueTime to match the next scheduled execution.

Most of the operations are scheduled to run every minute, not all toghether but delayed by some seconds each other.

Now, after adding a number of operations, about 30, it seems that the timers start to be inaccurate, starting the operations many seconds late, or even minutes late.

I am running the operation logic directly in the callback method of the timer, so the running thread should be the same as the timer.

Should I create a Task to run the operation instead of running it in the callback method to improve accuracy?

Or should I use a single timer with a fixed (1 second) dueTime to check which operations need to be started?
I don't like this last option because it would be more difficult to handle reentrancy..

Formentz
  • 1,083
  • 1
  • 14
  • 20
  • Hard to tell much without code. If you adjust timer at the end of every operation - it should not be late for _minutes_ with just 30 times (which is not much). – Evk Jun 05 '17 at 12:41

3 Answers3

1

Timers fire on a thread pool thread, so you are probably finding that as you add lots of timers that you are exhausting the thread pool.

You could increase the size of the thread pool, or alternatively ensure you have fewer timers than the thread pool size.

Firing off Tasks from the callback likely won't help - since you are going to be fighting for threads from the same thread pool. Unless you use long-running tasks.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • ThreadPool has 2047 workerThreads on target machine, and using Process Explorer I saw that the service runs 120-200 threads (looking at the 'Threads' tab). This makes me believe that it should not be a matter of ThreadPool – Formentz Jun 05 '17 at 14:12
  • How did you check that the thread pool had that many threads? What does calling ThreadPool.GetMinThreads() return? What about GetMaxThreads? Did you set the thread pool to have 2047 threads (and if so, can you show us how)? Did you use https://msdn.microsoft.com/en-us/library/system.threading.threadpool.setmaxthreads(v=vs.110).aspx ? – mjwills Jun 05 '17 at 21:49
  • Did you try using long-running tasks like I suggested? If so, what was the impact? Did the problem go away / get better or worse? – mjwills Jun 05 '17 at 21:52
  • To check the threads number I used Process Explorer by Sysinternals, do you know a tool to get the number of threads from a remote machine? The service is running on a server in the network. 2047 is the default value returned by GetMaxThreads, I never changed the value using SetMaxThreads. I left the execution in the thread of the timer callback method, so I didn't try to use long running tasks. Thanks for your help and sorry if I answer so late, but the service has to run for some hours (or days) to see the results – Formentz Jun 06 '17 at 08:38
  • The size of the ThreadPool returned by GetMaxThreads is 2047, now I am creating a method to read the values of GetMaxThreads, GetMinThreads and GetAvailableThreads at any time – Formentz Jun 06 '17 at 09:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/145942/discussion-between-formentz-and-mjwills). – Formentz Jun 06 '17 at 09:10
1

We usually setup multiple timers to handle different actions within a single service. We set the intervals and start, stop the timer on the Service Start/Stop/Shutdown events (and have a variable indicating the status for each one, i.e. bool Stopped) When the timer ticks over, we stop the timer, run the processing (which may take a while depending on the process, i.e. may take longer than the interval if its short.. (this code needs to be in a try--catch so it keeps going on errors) After the code has processed, we check the Stopped variable and if its not stopped we start the timer again (this handles the reentrancy that you've mentioned and allows the code to stick to the interval as much as possible) Timers are generally more accurate after about 100ms as far as I know, but should be close enough for what you want to do.

We have run this concept for years, and it hasn't let us down.

If you running these tasks as a sub-system of an ASP.NET app, you should also look at HangFire, which can handle background processing, eliminating the need for the windows service.

How accurate do the timers need to be? you could always use a single timer and run multiple processing threads at the same time? or queue the calls to some operations if less critical.

Mark Redman
  • 24,079
  • 20
  • 92
  • 147
0

Ok, I came to a decision: since I am not able to easily reproduce the behavior, I chose to solve the root problem and use the Service process to only:

  1. serve WCF requests done by clients
  2. schedule operations (which was problematic)

Every operation that could eat CPU is executed by another process, which is controlled directly by the main process (with System.Diagnostics.Process and its events) and communicates with it through WCF.

When I start the secondary process, I pass to it the PID of the main process through command line. If the latter gets killed, the Process.Exited event fires, and I can close the child process too.

This way the main service usually doesn't use much CPU time, and is free to schedule happily without delays.

Thanks to all who gave me some advices!

Formentz
  • 1,083
  • 1
  • 14
  • 20