Search code examples
c#multithreadingsingletontaskautoresetevent

Singleton - task inside constructor fails to start/ does not start asynchronically


I have a bit of weird problem that is hard to explain. I have singleton class where in the constructor I have to run a task to initialize some components/resources.

I used 2 implementation of singleton from C# in Depth and in one case everything is working fine, in another case - not.

Code is available below with some comments. The main problem that for some reason task is not started in one case, when I am using static field with initialier and static contructor (class Test2).

I made some other tests and looks like with the implementation 2 task does not start asynchronically, but starts synchronically after waiting time.

Implementation one. everything is working as expected

public sealed class Test1
{
    private static Test1 instance = null;
    private static readonly object padlock = new object();

    private Test1()
    {
        using (AutoResetEvent startEvent = new AutoResetEvent(false))
        {
            new Task(() => TaskThread(startEvent)).Start();

            if (!startEvent.WaitOne(1000))
            {
                throw new Exception("ERROR");
            }
        }
    }

    public int Result()
    {
        return 10;
    }

    private void TaskThread(AutoResetEvent startEvent)
    {
        //I am initializing some stuff here
        startEvent.Set();
    }

    public static Test1 Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Test1();
                }
                return instance;
            }
        }
    }
}

Implementation 2, task is never started, or started after waiting time of an event

public sealed class Test2
{
    private static readonly Test2 instance = new Test2();

    static Test2()
    {
    }
    private Test2()
    {
        using (AutoResetEvent startEvent = new AutoResetEvent(false))
        {
            new Task(() => TaskThread(startEvent)).Start();

            //here it fails to wait successfully and throws an
            //exception. Time limit is not reached
            if (!startEvent.WaitOne(1000))
            {
                throw new Exception("ERROR");
            }
        }
    }

    public int Result()
    {
        return 20;
    }

    private void TaskThread(AutoResetEvent startEvent)
    {
        //I am initializing some stuff here as well
        //but in this implementation code is never reached
        startEvent.Set();
    }

    public static Test2 Instance
    {
        get
        {
            return instance;
        }
    }
}

I am curious why is this happening and how to avoid this problems in future. Thanks a lot!


Solution

  • The root cause of such 'strange' behavior is pretty simple - CLR executes static constructor under a lock. That prevents created thread from entering TaskThread() method and setting startEvent to signaled state.

    After you face with such a problem and puzzle for several hours why this is happening, you start to understand why many sources advise not to use doubtful constructs like static constructors, global variables, etc.