#c_sharp #асинхронность
This question already has an answer here:
Зависает оператор `await` в оконном приложении / программа
висит при вызове Task.Result или Wait
(1 ответ)
Закрыт 3 года назад.
Есть некоторое недопонимание с работой асинхронного кода, в связи с чем прошу помощи.
Следующая функция вешает программу:
private List GetContractsDetail(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.
Комментариев нет:
Отправить комментарий