C # не может присоединиться к основному потоку - PullRequest
0 голосов
/ 26 июня 2018

У меня очень простой код, поведение которого я не могу понять.

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, а ThreadState равно Background, Stopped, WaitSleepJoin. Также удаление снов не имеет никакого значения.

В чем причина такого поведения? Что за тайна скрывается за методом Join() и выполнением метода Main?

1 Ответ

0 голосов
/ 26 июня 2018

Join() работает так, как вы ожидаете, проблема здесь заключается в предположении, что поток, выполняющий Main(), завершается, когда возвращается Main(), что не всегда так.

Ваш метод Main() вызывается платформой .NET, и когда метод возвращается, существует дополнительный код, выполняемый платформой перед выходом из основного потока (и, следовательно, процесса). В частности, одна из вещей, которые фреймворк делает как часть кода после Main, - это ожидание выхода всех потоков переднего плана.

По сути, это приводит к классической тупиковой ситуации: основной поток ожидает выхода вашего рабочего потока, а ваш рабочий поток ожидает выхода основного потока.

Конечно, если вы сделаете свой рабочий поток фоновым потоком (установив IsBackground = true перед его запуском), тогда код post-Main не будет ждать его выхода, что устраняет тупик. Однако ваш Join() будет все еще никогда не вернется, потому что, когда основной поток завершает работу, процесс также завершается.

Для получения более подробной информации о внутренностях фреймворка, которые выполняются до и после Main(), вы можете взглянуть на кодовую базу .NET Core на GitHub. Общий метод, который запускается Main(), равен Assembly::ExecuteMainMethod, а код, который запускается после возврата Main(), равен Assembly::RunMainPost.

...