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

Better task scheduling in Umbraco

ScheduledTasks are an antiquated way of handling scheduling events in .NET. Here's three better solutions you should be considering instead.

Posted: 1st June 2017

The best Umbraco packages of 2017

Discover some of the best Umbraco packages which can help you build Umbraco websites.

Posted: 2nd March 2017

Determining the length in seconds of a folder of MP3 files using NodeJS

NodeJS is a great quick and easy tool which allows you to quickly hack together any script which can save you time. In this post I show how I used Node to get the length in seconds of a bunch of MP3 files in the folder.

Posted: 8th January 2017