Мне кажется, что у вас есть фундаментальное неправильное понимание того, что ожидать от консольного приложения и модели потоков .NET-приложения.
Консольное приложение работает до тех пор, пока его поток ввода не завершит выполнение Main
метод.Это означает, что вы либо заняты потоком, выполняя работу, либо блокируете поток, пока другой сигнал не разблокирует его и не позволит методу завершиться.Это очень простая конструкция, предназначенная для того, чтобы позволить вам запустить приложение, выполнить некоторую работу, а затем завершить работу, когда вы закончите.Это означает, что поток никогда не «свободен» для выполнения другой работы;Вы не можете запланировать выполнение задачи в этом потоке без полного изменения модели планирования работ на более сложную.
В приложении на основе пользовательского интерфейса ваше приложение следует гораздо более сложной модели.Вместо того, чтобы приложение выполняло некоторую работу, а затем завершало работу, вместо этого основной поток просили прокачать очередь сообщений, выполняя работу на основе полученных сообщений и выходя из нее только тогда, когда оно получило сообщение «выход».Эта модель берет один поток и дает ему очередь работы, каждая часть работы сигнализируется сообщением.Это то, что позволяет вам иметь Task
резюме в потоке пользовательского интерфейса.
Такого рода путаница заключается в том, почему шаблон async void
обычно считается анти-шаблоном, и, конечно, почемудопустимо использовать только в приложениях с пользовательским интерфейсом.Приложение работает корректно только из-за очень специфической модели планирования задач.Любая другая модель вызывает разрушение этой конструкции, поглощая исключения и делая невозможным узнать, когда работа завершена.