Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task.Run in Static Initializer

Consider the following code.

static class X
{
    public static int Value = Task.Run(() => 0).Result;
}

class Program
{
    static void Main(string[] args)
    {
        var value = X.Value;
    }
}

Calling Task.Run and then Result in the static initializer causes the program to permanently freeze. Why?

like image 252
Timothy Shields Avatar asked Dec 23 '14 01:12

Timothy Shields


People also ask

Why static constructor is Parameterless in C#?

When a data member is shared among different instances it is imperative that data should be consistent among all the instances of the class. And also there is no way to call static constructor explicitly. Therefore the purpose of having a parameterized static constructor is useless.

Can we have constructor in static class?

Static classes cannot contain an instance constructor. However, they can contain a static constructor.

Can a constructor be static in C#?

A class or struct can only have one static constructor. Static constructors cannot be inherited or overloaded. A static constructor cannot be called directly and is only meant to be called by the common language runtime (CLR). It is invoked automatically.

When and how often is a static constructor called?

Static constructor are called automatically before the first instance is created or any static members are referenced. A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only.


1 Answers

You are seeing deadlock on the CLR's class initialization lock.

Basically, nothing in the class X can be used until the class is initialized. But your anonymous method () => 0 is compiled to a member of the class. The class initialization won't complete until the Task can complete, but the Task can't complete because it depends on a method that isn't allowed to run until the initialization of the class is complete.

Deadlock.

Your example is clearly contrived, so it's impossible to provide advice as to how to fix your real-world problem. In this particular example, you could replace the initialization with Task.FromResult(0).Result; but of course that's even more contrived; if that were actually usable, you'd just assign 0 to the field.

But whatever your real-world scenario is, the way to fix it is to not create a situation where initialization of the class depends on some external component needing that class for it to complete. You might consider, for example, using Lazy<T> to initialize the value, or to just call the method directly (which would be allowed).

Whether an example is contrived or not, there's never any point in starting a Task only to immediately block the current thread until it completes. So if you have any code that, while not literally exactly like this example, still does effectively the same thing, the obvious fix is to change it to execute in a serial, single-threaded manner.

like image 65
Peter Duniho Avatar answered Oct 22 '22 01:10

Peter Duniho