65.9K
CodeProject is changing. Read more.
Home

Static Constructor and Deadlock

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2 votes)

Oct 1, 2013

CPOL

1 min read

viewsIcon

8462

There is a potential opportunity of a deadlock if we perform any asynchronous blocking operation in a static constructor.

Static constructor is used to initialize the static members of a class. As per specification, static constructor is executed at most once in the given application domain and gets triggered by:

  • an instance of the class is created
  • any of the static members of the class are referenced

The CLR uses an internal lock to ensure that static constructor:

  • is only called once
  • gets executed before creation of any instance of the class or before accessing any static members.

With this behaviour of CLR, there is a potential opportunity of a deadlock if we perform any asynchronous blocking operation in a static constructor. Here is an example:

class Experiment
{
    public static readonly Experiment Instance;

    static Experiment()
    {
        Instance = new Experiment();
        Console.WriteLine("Initializing the instance on different thread");

        Thread thread = new Thread(() => Instance.Initialize());
        thread.Start();
        thread.Join();
        Console.WriteLine("Initialization completed");
    }

    void Initialize()
    {
        //Initializing members  
    }

    public void SayHello()
    {
        Console.WriteLine("Hello World");
    }
}

The main thread will wait for the helper thread to complete within the static constructor. Since the helper thread is accessing the instance method, it will first try to acquire the internal lock. As internal lock is already acquired by the main thread, we will end-up in a deadlock situation.

Even if the helper thread is not accessing any instance method, there is a risk that helper thread causes CLR to acquire the internal lock. Here is an example:

class Experiment
{
    static Experiment()
    {
        Thread thread = new Thread(()=>{});
        thread.Start();
        thread.Join();
    }
}

Another example with pLinq:

class Experiment
{
    public static readonly IEnumerable<int> SquaresOfFirst100Numbers;

    static Experiment()
    {
        Enumerable.Range(1, 100).AsParallel().Select(n => n * n).ToList();
    }
}

With this CLR behaviour, we should always avoid having any asynchronous blocking operation in static constructor as it can easily lead to a deadlock situation.