Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread-safety of System.Timers.Timer vs System.Threading.Timer

Tags:

In this article: http://msdn.microsoft.com/en-us/magazine/cc164015.aspx the author states that System.Threading.Timer is not thread-safe.

Since then this has been repeated on blogs, in Richter's book "CLR via C#", on SO, but this is never justified.

Moreover the MSDN documentation assures "This type is thread safe."

1) Who tells the truth?

2) If this is the original article what makes System.Threading.Timer not thread-safe and how its wrapper System.Timers.Timer achieves more thread-safety?

Thanks

like image 438
Pragmateek Avatar asked Oct 24 '13 21:10

Pragmateek


People also ask

Is system threading timer thread safe?

Timer is not thread-safe.

Is system timers timer multithreaded?

System. Timers. Timer , which fires an event and executes the code in one or more event sinks at regular intervals. The class is intended for use as a server-based or service component in a multithreaded environment; it has no user interface and is not visible at runtime.

What is System threading timer?

System.Threading. Timer Class. The Timer class (in the System. Threading namespace) is effective to periodically run a task on a separate thread. It provides a way to execute methods at specified intervals.

Does threading timer create a new thread?

Yes, it will create new threads.


2 Answers

No, that's not the way it works. The .NET asynchronous Timer classes are perfectly thread-safe. The problem with thread-safety is that it is not a transitive property, it doesn't make the other code that's executed thread-safe as well. The code that you wrote, not a .NET Framework programmer.

It is the same kind of problem with the very common assumption that Windows UI code is fundamentally thread-unsafe. It is not, the code inside Windows is perfectly thread-safe. The problem is all the code that runs that is not part of Windows and not written by a Microsoft programmer. There's always a lot of that code, triggered by a SendMessage() call. Which runs custom code that a programmer wrote. Or code he didn't write, like a hook installed by some utility. Code that assumes that the program doesn't make it difficult and just executes message handlers on one thread. He usually does, not doing that buys him a lot of trouble.

Same problem with the System.Timers.Timer.Elapsed event and the System.Threading.Timer callback. Programmers make lots of mistakes writing that code. It runs complete asynchronously on an arbitrary threadpool thread, touching any shared variable really does require locking to protect state. Very easy to overlook. And worse, much worse, very easy to get yourself into a pile of trouble when the code runs again, before the previous invocation stopped running. Triggered when the timer interval is too low or the machine is too heavily loaded. Now there are two threads running the same code, that rarely comes to a good end.

Threading is hard, news at eleven.

like image 192
Hans Passant Avatar answered Oct 06 '22 19:10

Hans Passant


The System.Timers.Timer class is not thread safe. Here is how it can be proved. A single Timer instance is created, and its property Enabled is toggled endlessly by two different threads that are running in parallel. If the class is thread safe, its internal state will not be corrupted. Lets see...

var timer = new System.Timers.Timer(); var tasks = Enumerable.Range(1, 2).Select(x => Task.Run(() => {     while (true)     {         timer.Enabled = true;         timer.Enabled = false;     } })).ToArray(); Task.WhenAny(tasks).Unwrap().GetAwaiter().GetResult(); 

This program is not running for too long. An exception is thrown almost immediately. It is either a NullReferenceException or an ObjectDisposedException:

System.NullReferenceException: Object reference not set to an instance of an object.    at System.Timers.Timer.UpdateTimer()    at System.Timers.Timer.set_Enabled(Boolean value)    at Program.<>c__DisplayClass1_0.<Main>b__1()    at System.Threading.Tasks.Task`1.InnerInvoke()    at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown ---    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location where exception was thrown ---    at Program.Main(String[] args) Press any key to continue . . .  System.ObjectDisposedException: Cannot access a disposed object.    at System.Threading.TimerQueueTimer.Change(UInt32 dueTime, UInt32 period)    at System.Threading.Timer.Change(Int32 dueTime, Int32 period)    at System.Timers.Timer.UpdateTimer()    at System.Timers.Timer.set_Enabled(Boolean value)    at Program.<>c__DisplayClass1_0.<Main>b__1()    at System.Threading.Tasks.Task`1.InnerInvoke()    at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown ---    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location where exception was thrown ---    at Program.Main(String[] args) Press any key to continue . . . 

The reason this happens is quite evident, after studying the source code of the class. There is no synchronization when the internal fields of the class are changed. So synchronizing manually the access to a Timer instance is mandatory, when this instance is mutated by multiple threads in parallel. For example the program below runs forever without throwing any exception.

var locker = new object(); var timer = new System.Timers.Timer(); var tasks = Enumerable.Range(1, 2).Select(x => Task.Run(() => {     while (true)     {         lock (locker) timer.Enabled = true;         lock (locker) timer.Enabled = false;     } })).ToArray(); Task.WhenAny(tasks).Unwrap().GetAwaiter().GetResult(); 

Regarding the System.Threading.Timer class, it has no properties, and its single method Change can be called by multiple threads in parallel without any exceptions thrown. Its source code indicates that it's thread safe, since a lock is used internally.

like image 24
Theodor Zoulias Avatar answered Oct 06 '22 20:10

Theodor Zoulias