Static Constructor and Deadlock






4.50/5 (2 votes)
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.