#c_sharp #асинхронность
This question already has an answer here: Зависает оператор `await` в оконном приложении / программа висит при вызове Task.Result или Wait (1 ответ) Закрыт 3 года назад. Есть некоторое недопонимание с работой асинхронного кода, в связи с чем прошу помощи. Следующая функция вешает программу: private ListGetContractsDetail(contracts) { List contractsList = new List (); // Сюда буду ложить ответы Task[] requests = new Task[contracts.Length]; // Создаю массив для моих тасков Uri Uri = new Uri(Settings.CONTRACT_DETAIL_URI); for (int i = 0; i < contracts.Length; i++) // По каждому контракту { HttpRequest ContractRequest = new HttpRequest(); //Это мой класс для работы с HttpClient Dictionary data = new Dictionary () { ["access_token"] = token, ["contract_id"] = contracts[i] }; // Данные для GET запроса requests[i] = ContractRequest.Get(Uri, data); // Моя обертка над HttpClient.GetAsync } Task.WaitAll(requests); // Тут зависает программа // Тут еще код, который я не дописал return contractsList; } По информации в интернете не до конца понимаю: работает ли WaitAll как await? И если да, то почему виснет программа? стартуют ли вообще мои Таски? в каком месте кода я не прав? =)
Ответы
Ответ 1
Нет. Task.Wait, Task.WaitAll - это совсем не await! await - это асинхронное продолжение, Wait же является синхронным ожиданием. У вас взаимоблокировка. Вы работаете в потоке UI - а потому все ваши задачи свои продолжения ставят в очередь событий. Пока эта очередь не "провернется" - задача не будет выполнена. Но саму очередь вы при этом заблокировали синхронным ожиданием! Правильный способ избавиться от взаимоблокировки - использовать асинхронный код на всех уровнях. Да, для этого саму функцию GetContractsDetail нужно тоже сделать асинхронной: await Task.WhenAll(requests); Костыльный, но более простой способ - вынести задачи в пул потоков: requests[i] = Task.Run(() => ContractRequest.Get(Uri, data)); Еще один костыльный и простой способ - проставить .ConfigureAwait(false) на первом операторе await в методе ContractRequest.GetОтвет 2
WaitAll работает не так, как await. WaitAll блокирует текущий поток до завершения тасков. await освобождает текущий поток, и ставит остаток метода в continuation к таску в соответствующем контексте (GUI-потоке или потоке с тем же HttpContext). Соответственно, если код внутри вашей обертки решит поставить таск на выполнение в текущем контексте (GUI или ASP.NET), то он будет ждать освобождения контекста. А WaitAll будет держать поток (и контекст), ожидая окончания выполнения кода - и все подвиснет. Это достаточно подробно раскрыто в MSDN Magazine, Async/Await - Best Practices in Asynchronous Programming / Async All the Way Не стоит смешивать async/await и прямые Wait.
Комментариев нет:
Отправить комментарий