Страницы

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

воскресенье, 22 декабря 2019 г.

Асинхронный код (Task.WaitAll) вешает программу [дубликат]

#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.

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

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