Страницы

Поиск по вопросам

понедельник, 14 января 2019 г.

Использование async/await в консольном приложении

Всем привет. Запутался немного в коде. Почему поток Main завершается до запуска этого кода ?
await Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(4000); Console.WriteLine("Pavel"); });
Решил попробовать в Main приписать к вызову метода Math1 слово await но тогда код вообще не компилируется. А если его убрать, то выходит , что Main завершился мгновенно и дело не дошло до указанного выше блока кода. Привожу весь код
using System; using System.Threading.Tasks; class a { public async Task Meth1() { await Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(4000); Console.WriteLine("Pavel"); }); } } class b { static void Main() { a A = new a(); await A.Meth1(); //без await сразу все завершается, с ним не компилится } }
Почему так происходит и как решить такую ситуацию ? Я ожидаю от кода такое поведение, что при вызове Meth1 Main ожидает пока в Meth1 выполнится лямбда


Ответ

Обновление. Начиная с C# 7.1, модификатор async можно использовать также и для функции Main. При этом неудобство, описанное ниже (отсутствует SynchronizationContext по умолчанию, а значит, код после await не будет возвращаться в исходный поток), остаётся.

Смотрите.
Если вы используете async/await, приложение командной строки — не лучший выбор. К сожалению, C# из коробки не поддерживает async для функции Main (обсуждение на гитхабе, вы можете тоже отписаться, чтобы было понятно, что эта фича нужна).
Поэтому вы должны писать что-то вроде такого:
class Program { static async Task AsyncMain(string[] args) { await Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(4000); Console.WriteLine("Pavel"); }); }
static void Main(string[] args) { // синхронное ожидание! AsyncMain(args).Wait(); } }
Другая проблема состоит в том, что в приложениях командной строки нету SynchronizationContext'а, который мог бы «вернуть» выполнение в начальный поток. Потому что для этого нужен в той или иной форме аналог message loop'а, а у вас его нету. Таким образом, поведение программы будет отличаться от того, что обычно бывает в WPF/WinForms: код после await не будет возвращаться в исходный поток!
Как бороться с этим? Простейший обходной путь — можно тренироваться не на приложении командной строки, а на графическом приложении.
Более хороший путь — воспользоваться существующим message loop'ом. Например, так:
/* Обвязочный код для использования async/await в консольных приложениях на C# Создаёт SynchronizationContext, который возвращает async-вызовы в главный поток (в отличие от стандартного контекста, который бы использовался в консольных приложениях) Идея VladD, используйте и переделывайте как хотите. Никаких гарантий, разумеется. Требует подключения WindowsBase.dll */ class Program { static async Task AsyncMain(string[] args) { // тут стартует ваша программа, можете пользоваться async/await // не забудьте про return, например: return 0; }
[STAThread] static int Main(string[] args) { int result = -1; var dispatcher = Dispatcher.CurrentDispatcher; dispatcher.BeginInvoke((Action)(() => { AsyncMain(args).ContinueWith(asyncMain => { if (!asyncMain.IsCanceled && !asyncMain.IsFaulted) result = asyncMain.Result; dispatcher.BeginInvokeShutdown(DispatcherPriority.ApplicationIdle); }, TaskScheduler.FromCurrentSynchronizationContext()); })); Dispatcher.Run(); return result; } }

Комментариев нет:

Отправить комментарий