C# .NET timers and multithreading

As noticed by Joe Albahari in his article about timers – http://www.albahari.com/threading/part3.aspx#_Timers they give us a great ability to use memory and resources more effectively compared with approach in which we create thread that contains some code like this:

volatile static bool keepGoing = true;

while (keepGoing)

{

	// do something
	if something bad occurred somewhere in code
	
	keepGoing = false;
}

The same idea is described by Jeffry Richter in his CLR via C# book (take a look at “Performing a Periodic Compute-Bound Operation” chapter). 

What does this mean? With infinity loop you can have problem with such things like resources leaks and so on. Using timers, a thread is finished with freeing resources you forgot to free and new thread will be created and do the same work again.

The first example that comes in mind is WPF application with separate thread which should periodically monitor something or repeat some sequence of actions. To do that we can use (with infinite loop):

  • BackgroundWorker
  • Thread
  • Task
  • Async

Of course we should not forget about handling errors and realizing cancelation ability in our threads that make work more complex.

Let’s return to the timers and take a closet look at them.

As mentioned in article described about there are four types of timers:

  • general-purpose multithreading timers;
  • special-purpose single-threaded timers.

To the first group of timers belong classes:

  • System.Threading.Timer
  • System.Timers.Timer

Both of them use thread pool and System.Timers.Timer is wrapper around System.Threading.Timer. Here is simple console application that show you how  System.Threading.Timer works

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            Timer t = new Timer(testDelegate,null, 0, 250);

            Console.ReadLine();

        }

        static void testDelegate(object obj)
        {
            System.Diagnostics.Trace.WriteLine("call to testDelegate, thread id: " + Thread.CurrentThread.ManagedThreadId + " , is tp: " + Thread.CurrentThread.IsThreadPoolThread);
        }
    }
}

Trace output of execution will be something like shown below

image

As you can see, testDelegate is called in different threads from the thread pool. As a result:

  • we can’t change UI elements from WPF or Windows Forms directly from a timer’s handler using these types of timers (System.Timers.Timer also uses thread pool)  without any synchronization mechanism;
  • we need to think about how to write code of timer’s handler (delegate) in thread-safe manner, because a new event can be fired when an old one is not finished yet.

I recommend you take a look at source code of .NET System.Threading.Timerhttps://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs. to get detailed information about how Timer class work. In brief:

  • there is only one queue (class TimerQueue) in the  AppDomain realized as singleton pattern that maintains all active timers (class TimerQueueTimer). Queue is realized as an unordered doubly-linked list due needing of good performance for inserting and deleting operations
  • each time we create new timer it is added to the queue
  • AppDomainTimerCallback is called by VM when the native timer fires.  I think native timer is some kernel object that can be accessed trough Win API and used to provide all necessary functionality to managed code
  • AppDomainTimerCallback  calls FireNextTimers method that walks trough linked list gets timer object (TimerQueueTimer) and checks if is there need to call to Fire method. It gives the first available timer object and fires on this thread (timerToFireOnThisThread.Fire()). Other timers will be added to the thread pool (QueueTimerCompletion(timer))
  • Fire method is just wrapper under CallCallback method which executes our timer’s handler (to which point m_timerCallback).

And here is source code of System.Timers.Timer https://referencesource.microsoft.com/#System/services/timers/system/timers/Timer.cs

This class doesn’t use inheritance but composition to provide System.Threading.Timer functionality as shown below.

image

And here is point in code where timer member will be set by System.Threading.Timer object value

timer = new System.Threading.Timer(callback, cookie, i, autoReset? i:Timeout.Infinite);

System.Timers.Timer provides additional functionality described here – http://www.albahari.com/threading/part3.aspx#_Timers and on MSDN. It is interesting that Jeffry doesn’t recommend to use this class because it probably should have been removed.

Second groups of timers developed to simplify work with timers in WPF and Windows Forms application:

  • System.Windows.Threading.DispatcherTimer (WPF)
  • System.Windows.Forms.Timer (Windows Forms)

They are very similar to System.Timers.Timer by their members but they use message  pumping approach instead Thread Pool. As a result  code of timer’s handler is executed in the same thread as UI code. Disadvantage of this is inability to run long time code in handler without blocking UI thread and freeze UI interface as the result.

After short analyzing of timers let’s return to our goal  – replace a thread with infinite loop with timer’s handler that periodically called by timer. Here is simple WFP application with bug.

  public partial class MainWindow : Window
    {
        
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

            Timer timer = new Timer(testTimerCode, null, 0, 3000);

        }

        private static void testTimerCode(object obj)
        {
            System.Diagnostics.Trace.WriteLine("test timer tick");

        }
    }

As shown below timer’s handler will be called only 2 times

image

The bug is here in line

Timer timer = new Timer(testTimerCode, null, 0, 3000);

The reason is that when timer variable become invisible garbage collector stops timer. Detailed explanation of this behavior you can find in CLR via C# book by Jeffrey Richter. Correct using is shown below:

 public partial class MainWindow : Window
    {
        private static Timer timer;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            StartTest.IsEnabled = false;
            timer = new Timer(testTimerCode, null, 0, 3000);

        }

        private static void testTimerCode(object obj)
        {
            System.Diagnostics.Trace.WriteLine("test timer tick");

        }
    }

This timer works until you stop it. The next problem with System.Threading.Timer is that it can be called more than once and as a result 2 or more threads can execute code of thread’s handler simultaneously. This problem is described by example below:

    public partial class MainWindow : Window
    {
        private static Timer timer;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            StartTest.IsEnabled = false;
            timer = new Timer(testTimerCode, null, 0, 3);

        }

        private static void testTimerCode(object obj)
        {
            System.Diagnostics.Trace.WriteLine("test timer tick, " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);

        }
    }

A result of execution can be following

image

Because code of testTimerCode is being executed now longer and timer is firing new thread more frequently  – more than one thread of thread pool  are being executed at the same time. A great workaround is described in Jeffry’s book:

public partial class MainWindow : Window
    {
        private static Timer timer;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            StartTest.IsEnabled = false;
            timer = new Timer(testTimerCode, null, Timeout.Infinite, Timeout.Infinite);

            timer.Change(0, Timeout.Infinite);

        }

        private static void testTimerCode(object obj)
        {
            System.Diagnostics.Trace.WriteLine("test timer tick, " + Thread.CurrentThread.ManagedThreadId);
            // simulate time-consuming work
            Thread.Sleep(2000);
            timer.Change(3000, Timeout.Infinite);
        }
    }

So, if you need to run some task every 5 minuets for example just change first argument of Change method in testTimerCode handler as shown below:

timer.Change(5 * 60 * 1000, Timeout.Infinite);

And don’t forget about error handling in testTimerCode handler. If you test new code you see that thread is finished and then new one start again.

image

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s