C#无法加入主线程

时间:2018-06-26 18:40:25

标签: c# .net multithreading join main

我有非常简单的代码,我无法理解其行为。

class Program
{
    static void Main(string[] args)
    {
        // Get reference to main thread
        Thread mainThread = Thread.CurrentThread;

        // Start second thread
        new Thread(() =>
        {
            Console.WriteLine("Working...");
            Thread.Sleep(1000);

            Console.WriteLine("Work finished. Waiting for main thread to end...");
            mainThread.Join();        // Obviously this join cannot pass

            Console.WriteLine("This message never prints. Why???");
        }).Start();

        Thread.Sleep(300);
        Console.WriteLine("Main thread ended");
    }
}

这个永无止境的程序的输出是:

Working...
Main thread ended
Work finished. Waiting for main thread to end...

为什么线程的代码卡在Join()方法调用中?通过其他打印输出可以发现,在调用Join()之前,IsAlive的属性mainThread已设置为false,而ThreadStateBackground, Stopped, WaitSleepJoin。同样,消除睡眠也没有任何区别。

此行为的原因是什么? Join()方法和Main方法的执行有什么奥秘?

2 个答案:

答案 0 :(得分:3)

Join()可以按预期工作,这里的问题是假设运行Main()的线程在Main()返回时终止,但并非总是如此。

您的Main()方法由.NET框架调用,当该方法返回时,框架将在主线程(并因此退出进程)退出之前执行其他代码。具体地说,该框架作为后Main代码的一部分要做的一件事就是等待所有前台线程退出。

这实际上会导致典型的死锁情况-主线程正在等待您的工作线程退出,而工作线程正在等待主线程退出。

当然,如果您将工作线程设为后台线程(通过在启动线程之前设置IsBackground = true),则后主代码将不等待其退出,从而消除了死锁。但是,您的Join() still 永不返回,因为当主线程退出时,该进程也会退出。

有关在Main()之前和之后运行的框架内部的更多详细信息,您可以查看GitHub上的.NET Core代码库。运行Main()的总体方法是Assembly::ExecuteMainMethod,而在之后 Main()返回的代码是Assembly::RunMainPost

答案 1 :(得分:-1)

我认为您理解Join很好。

据我了解,C#中的主线程很特殊。主线程调用您的Main方法后,执行返回到框架,该框架等待进程中的其他子线程终止。

编辑:这是一个示例,显示您编写的代码在您不是主线程的情况下有效。

static void Main(string[] args)
{
    Thread primaryThread = null;

    primaryThread = new Thread(() =>
    {
        // Start second thread
        new Thread(() =>
        {
            Console.WriteLine("Working...");
            Thread.Sleep(1000);

            Console.WriteLine("Work finished. Waiting for primary thread to end...");
            primaryThread.Join();

            Console.WriteLine("This message does print.");
        }).Start();

        Thread.Sleep(300);
        Console.WriteLine("primary thread ended");
    });

    primaryThread.Start();
}