Страницы

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

четверг, 2 января 2020 г.

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

#c_sharp #многопоточность


Всем привет. Запутался немного в коде. Почему поток 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  выполнится лямбда
    


Ответы

Ответ 1



Обновление. Начиная с 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; } }

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

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