#winforms #asynchronous #async-await #event-handling #.net-5
Вопрос:
У меня есть асинхронный метод, подобный этому:
private async Task TaskAsync() { await Task.Run(() => Task.Delay(2000)); }
Затем я вызываю его в событии нажатия кнопки, которое я объявил следующим образом:
private async void button1_Click(object sender, EventArgs e) { await TaskAsync(); MessageBox.Show("Afterwards."); }
Теперь, когда я нажимаю на кнопку, TaskAsync()
ее буквально ждут, и окно сообщения не отображается до тех пор, пока TaskAsync()
не завершится выполнение. Однако, когда я удаляю await
команду при вызове TaskAsync()
события click, выполнение немедленно переходит в окно сообщения.
Я что-то здесь делаю не так? Является ли это нормальным поведением асинхронности…ждать?
Мой проект представляет собой проект .NET Core 5 C# winform.
Комментарии:
1. Две вещи неправильны. Во-первых,
TaskAsync
метод используетсяTask.Run
для запуска другого асинхронного метода. Это просто пустая трата времени. Это должно быть справедливоawait Task.Delay(2000);
. Во-вторых, если вы не дождетесь выполнения задачи, выполнение начнется немедленно. В этом весь смысл использованияawait
— ожидание завершения уже выполняемой асинхронной задачи
Ответ №1:
Здесь есть две проблемы. Во-первых, TaskAsync
метод используется Task.Run
для запуска другого асинхронного метода. Это просто пустая трата времени. Это должно быть просто :
private async Task TaskAsync() { await Task.Delay(2000); }
если нет
private Task TaskAsync()=>Task.Delay(2000);
Во-вторых, если Задача не будет выполнена, ее выполнение начнется немедленно. В этом весь смысл использования await
— ожидание завершения уже выполняемой асинхронной задачи без блокировки вызывающего потока.
Исходный код эквивалентен :
private async void button1_Click(object sender, EventArgs e) { await Task.Delay(2000); MessageBox.Show("Afterwards."); }
Без await
этого задача , возвращенная пользователем Task.Delay()
, будет проигнорирована, и окно сообщения будет немедленно отображено.
Если вы хотите начать длительную операцию, например, чтение большого файла, и одновременно отобразить сообщение, задача может быть сохранена в поле и ожидаться после закрытия диалогового окна:
private async void button1_Click(object sender, EventArgs e) { var task=File.ReadAllTextAsync(...); MessageBox.Show("Reading a file"); var text=await task; MessageBox.Show("Afterwards."); }
или
private async void button1_Click(object sender, EventArgs e) { var task=Task.Run(()=>SomeBackgroundProcessing(someArgs)); MessageBox.Show("Processing"); var text=await task; MessageBox.Show("Afterwards."); }
Комментарии:
1. Спасибо, @PanagiotisKanavos. Да, теперь я понял, как работает асинхронное ожидание. Я просто привык использовать потоки и сразу переходить к выполнению следующего оператора; Мне придется привыкнуть к этому. Кроме того, причина в
await Task.Run(() => Task.Delay(2000));
том, что это быстрая сокращенная версия реального кода. 🙂2. @ocm в этом случае вы должны опубликовать реальный код. Многие API имеют асинхронные альтернативы, особенно методы, связанные с вводом-выводом (например, HttpClient. GetAsync, асинхронная синхронизация ToListAsync()EF
. Blocking with
в WinForms может быть решена с.ConfigureAwait(false)
помощью .Task.Run
обычно требуется для связанной с процессором части метода.3. параллелизм @ocm с потоками затруднен, когда вам приходится выполнять более 2 или 3 операций — кроме того, для ввода-вывода не нужны потоки. Вот почему многие языки приняли обещания, фьючерсы и даже
async/await
ключевые слова, чтобы скрыть эту сложность, например JavaScript, Kotlin, C , Rust.4. Привет @PanagiotisKanavos, не важно, что
TaskAsync()
на самом деле должно было быть сделано, моя проблема заключалась в необходимости одновременного выполнения следующего оператора (в моем примере показано окно сообщения). Я не согласен с тем, что IO не нуждается в потоках. Потоки-это просто способ обработки параллелизма, и пользователь может использовать их или выполнять асинхронное ожидание.5. @ocm Я не говорю о том, как код вызывает операцию ввода-вывода. Ввод-вывод означает, что вы всегда ждете ответа от кого-то другого, поэтому нет необходимости в том, чтобы активный поток что-либо делал. Процессор просто ждет. В Windows сама ОС всегда использует асинхронный ввод-вывод и обратные вызовы вместо блокировки потоков. Моделируются блокирующие операции. Когда вы читаете файл или выполняете HTTP-вызов, ОС начнет операцию и немедленно вернется. Когда эта операция завершится, она вызовет метод обратного вызова.