André Miranda
André Miranda

Reputation: 6588

.NET - Multiple Timers instances mean Multiple Threads?

I already have a windows service running with a System.Timers.Timer that do a specific work. But, I want some works to run at the same time, but in different threads.

I've been told to create a different System.Timers.Timer instance. Is this correct? Is this way works running in parallel?

for instance:

System.Timers.Timer tmr1 = new System.Timers.Timer();
tmr1.Elapsed += new ElapsedEventHandler(DoWork1);
tmr1.Interval = 5000;

System.Timers.Timer tmr2 = new System.Timers.Timer();
tmr2.Elapsed += new ElapsedEventHandler(DoWork2);
tmr2.Interval = 5000;

Will tmr1 and tmr2 run on different threads so that DoWork1 and DoWork2 can run at the same time, i.e., concurrently?

Thanks!

Upvotes: 2

Views: 11268

Answers (4)

YK1
YK1

Reputation: 7602

Will tmr1 and tmr2 run on different threads so that DoWork1 and DoWork2 can run at the same time, i.e., concurrently?

At the start, yes. However, what is the guarantee both DoWork1 and DoWork2 would finish within 5 seconds? Perhaps you know the code inside DoWorkX and assume that they will finish within 5 second interval, but it may happen that system is under load one of the items takes more than 5 seconds. This will break your assumption that both DoWorkX would start at the same time in the subsequent ticks. In that case even though your subsequent start times would be in sync, there is a danger of overlapping current work execution with work execution which is still running from the last tick.

If you disable/enable respective timers inside DoWorkX, however, your start times will go out of sync from each other - ultimately possible they could get scheduled over the same thread one after other. So, if you are OK with - subsequent start times may not be in sync - then my answer ends here.

If not, this is something you can attempt:

static void Main(string[] args)
        {
            var t = new System.Timers.Timer();
            t.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;

            t.Elapsed += (sender, evtArgs) =>
            {
                var timer = (System.Timers.Timer)sender;
                timer.Enabled = false; //disable till work done

                // attempt concurrent execution
                Task work1 = Task.Factory.StartNew(() => DoWork1());
                Task work2 = Task.Factory.StartNew(() => DoWork2());


                Task.Factory.ContinueWhenAll(new[]{work1, work2}, 
                            _ => timer.Enabled = true); // re-enable the timer for next iteration
            };

            t.Enabled = true;

            Console.ReadLine();
        }

Upvotes: 4

Hans Passant
Hans Passant

Reputation: 941327

It is not incorrect.

Be careful. System.Timers.Timer will start a new thread for every Elapsed event. You'll get in trouble when your Elapsed event handler takes too long. Your handler will be called again on another thread, even though the previous call wasn't completed yet. This tends to produce hard to diagnose bugs. Something you can avoid by setting the AutoReset property to false. Also be sure to use try/catch in your event handler, exceptions are swallowed without diagnostic.

Upvotes: 6

Kevin Anderson
Kevin Anderson

Reputation: 7010

Kind of. First, check out the MSDN page for System.Timers.Timer: http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

The section you need to be concerned with is quoted below:

If the SynchronizingObject property is null, the Elapsed event is raised on a ThreadPool thread. If processing of the Elapsed event lasts longer than Interval, the event might be raised again on another ThreadPool thread. In this situation, the event handler should be reentrant.

Basically, this means that where the Timer's action gets run is not such that each Timer has its own thread, but rather that by default, it uses the system ThreadPool to run the actions.

If you want things to run at the same time (kick off all at the same time) but run concurrently, you can not just put multiple events on the elapsed event. For example, I tried this in VS2012:

    static void testMethod(string[] args)
    {
        System.Timers.Timer mytimer = new System.Timers.Timer();
        mytimer.AutoReset = false;
        mytimer.Interval = 3000;

        mytimer.Elapsed += (x, y) => {
            Console.WriteLine("First lambda.  Sleeping 3 seconds");
            System.Threading.Thread.Sleep(3000);
            Console.WriteLine("After sleep");
        };
        mytimer.Elapsed += (x, y) => { Console.WriteLine("second lambda"); };
        mytimer.Start();
        Console.WriteLine("Press any key to go to end of method");
        Console.ReadKey();
    }

The output was this:

Press any key to go to end of method

First lambda.

Sleeping 3 seconds

After sleep

second lambda

So it executes them consecutively not concurrently. So if you want "a bunch of things to happen" upon each timer execution, you have to launch a bunch of tasks (or queue up the ThreadPool with Actions) in your Elapsed handler. It may multi-thread them, or it may not, but in my simple example, it did not.

Try my code yourself, it's quite simple to illustrate what's happening.

Upvotes: 2

Jim Mischel
Jim Mischel

Reputation: 133975

Multiple timers might mean multiple threads. If two timer ticks occur at the same time (i.e. one is running and another fires), those two timer callbacks will execute on separate threads, neither of which will be the main thread.

It's important to note, though, that the timers themselves don't "run" on a thread at all. The only time a thread is involved is when the timer's tick or elapsed event fires.

On another note, I strongly discourage you from using System.Timers.Timer. The timer's elapsed event squashes exceptions, meaning that if an exception escapes your event handler, you'll never know it. It's a bug hider. You should use System.Threading.Timer instead. System.Timers.Timer is just a wrapper around System.Threading.Timer, so you get the same timer functionality without the bug hiding.

See Swallowing exceptions is hiding bugs for more info.

Upvotes: 5

Related Questions