Синхронный вызов асинхронной функции более отзывчив

#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-вызов, ОС начнет операцию и немедленно вернется. Когда эта операция завершится, она вызовет метод обратного вызова.