Наблюдая за тем, что происходит с отладчиком, процесс ожидает Task.Yield () и никогда не переходит к возвращению form.ShowDialog (), и, таким образом, вы никогда не увидите RunningForm.Затем процесс переходит к LoadDataAsync () и навсегда зависает в ожидании Task.Delay (2000).
Почему это происходит?
Что происходит здесь, когда вы делаете var runningForm = new RunningForm()
в потоке консоли без какого-либо контекста синхронизации (System.Threading.SynchronizationContext.Current
имеет значение null), он неявно создает экземпляр WindowsFormsSynchronizationContext
и устанавливает его в текущий поток, подробнее об этом здесь .
Затем, когда вы нажимаете await Task.Yield()
, метод ShowDialogAsync
возвращается вызывающей стороне, и продолжение await
публикуется в этом новом контексте синхронизации.Однако у продолжения никогда не будет возможности быть вызванным, потому что текущий поток не запускает цикл сообщений и отправленные сообщения не перекачиваются.Нет тупика, но код после await Task.Yield()
никогда не выполняется, поэтому диалог даже не отображается.То же самое относится и к await Task.Delay(2000)
.
Мне больше интересно узнать, почему он работает для WinForms, а не для консольных приложений.
Вам нужен поток пользовательского интерфейсас циклом сообщений в вашем консольном приложении.Попробуйте рефакторинг консольного приложения следующим образом:
public void Run()
{
var runningForm = new RunningForm();
runningForm.Loaded += async delegate
{
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
};
System.Windows.Forms.Application.Run(runningForm);
}
Здесь задача Application.Run
- запустить модальный цикл сообщений (и установить WindowsFormsSynchronizationContext
в текущем потоке), а затем показать форму.Асинхронный обработчик runningForm.Loaded
вызывается в этом контексте синхронизации, поэтому логика внутри него должна работать точно так же, как и ожидалось.
Это, однако, делает Test.Run
синхронным методом , т.е.возвращается только тогда, когда форма закрыта и цикл сообщений завершен.Если это не то, что вам нужно, вам нужно создать отдельный поток для запуска вашего цикла сообщений, что-то вроде того, что я делаю с MessageLoopApartment
здесь .
Тем не менее, в типичных приложениях WinForms или WPF почти никогда не требуется дополнительный поток пользовательского интерфейса.