Skip to content
Posted: 26th October 2016

Thread safety sounds complicated, but with a couple of examples, a simple understanding of how threads work can be made.

This became an area of interest to me recently when some strange behaviour occured in a program that I had helped write. The program was originally written as a simple static class, but as time went on, more and more features were added, until the class was a monstrosity. We've all been there...

Eventually thread safety became a problem with the program because a second process (or thread) of the static method was called while the first method was still running.

Here is a simplified example of the buggy program:

namespace StaticTest
{
    public class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                // Run the method asynchronously.
                new Task(Worker.Work).Start();

                Thread.Sleep(5000);

                new Task(Worker.Work).Start();

                Console.ReadLine();
            }
        }
    }

    /// <summary>
    /// A class that will do "work".
    /// </summary>
    public static class Worker
    {
        /// <summary>
        /// A simple logging variable which has text appended to it.
        /// </summary>
        public static string Log;

        /// <summary>
        /// An indicator that stops the process from being ran twice.
        /// </summary>
        public static bool InProgress;

        /// <summary>
        /// A fake method that will work for 10 seconds before logging.
        /// </summary>
        public static void Work()
        {
            Log = "";

            var startTime = DateTime.Now;

            Log += "Started at: " + startTime.ToString() + Environment.NewLine;

            if (InProgress)
            {
                Log += "Process already started before. Exiting out." + Environment.NewLine;

                return;
            }

            InProgress = true;

            Log += "Doing work for 10 seconds..." + Environment.NewLine;

            Thread.Sleep(10000);

            Log += "Finished work." + Environment.NewLine;

            Log += "Ended at: " + DateTime.Now.ToString() + Environment.NewLine;

            Console.Write(Log);

            InProgress = false;
        }
    }
}

This program had a little bit of consideration put into it to ensure some simple safety:

  • the log was set to an empty string when the Work() method was called
  • an InProgress boolean is set to true to ensure that the work can't be done twice at the same time

I expected the following to be logged to the terminal:

Started at: 26/10/2016 20:11:27
Finished work.
Ended at: 26/10/2016 20:11:27

But instead, this was logged:

Started at: 26/10/2016 20:11:27
Process already started before. Exiting out.
Finished work.
Ended at: 26/10/2016 20:11:32

This can be a little bit confusing, but as it turns out, the static properties on a static class are not thread safe. What this means is that the property is shared between threads.

Here's a step by step on what happened:

  • Main started running
  • the Work method was ran (on a new thread, which we will call "Work (1)")
  • Main then paused on Thread.Sleep(5000)
  • Work (1) continued and set the Log string to empty. It then added "Started at: 26/10/2016 20:11:27"
  • Process.InProgress was currently set to false, so Work skipped the if block
  • Process.InPogress was set to true
  • "Doing work for 10 seconds..." is added to the log
  • Work (1) then waited at Thread.Sleep(10000)
  • Main finished waiting and started a new thread for the next Work (2) method
  • Work (2) sets the Log string to empty. It then added "Started at: 26/10/2016 20:11:32"
  • Work (2) saw that the InProgress boolean was set to true, and added "Process already started before. Exiting out." to the Log
  • Work (1) finished waiting added "Finished work." and "Ended at: 26/10/2016 20:11:32" to the Log
  • Work (1) writes the Log to the console

This program can be fixed by either making the Worker class non-static (along with the properties and methods inside of it), or by making the Log thread static. This can be done with a simple attribute:

[ThreadStatic]
public static string Log;

The moral of the story

Generally in a majority of my classes I won't use static classes. Although static classes are quicker to instantiate (in terms of processing time and developer writing time), they're not exactly perfect for all situations.

The only time I would consider using a static class is if I want to implement a helper method - such as changing a DateTime into a specific format. These kinds of methods can be useful, but can be slightly annoying to call if a class has to be instantiated.

Related blog posts

Making Umbraco Nodes with the Models Builder

In my latest Umbraco website built I have been getting friendly with the new Models Builder that is included by default. In this post I show how the Models Builder can be used with the Content Service to create new Umbraco nodes programmatically.

Posted: 20th March 2016

How to render MVC emails with CSHTML

Tired of using string formatting to create emails in your code? Read here to find out about how to build HTML emails using MVC, Razor and CSHTML.

Posted: 6th February 2016

Caching Umbraco web API controllers

Caching improves load time for everyone. Here I explain how easily make caching work with Web API controllers.

Posted: 26th January 2016