Асинхронный вызов с ожиданием в HttpClient никогда не возвращается

Мне звонят из C# metro-приложения на базе xaml на Win8 CP; этот вызов просто попадает в веб-службу и возвращает данные JSON.

HttpMessageHandler handler = new HttpClientHandler();

HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");

var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);

Он зависает на await, но вызов http фактически возвращается почти сразу (подтверждено через скрипач); это как будто await игнорируется и просто висит там.

Прежде чем вы спросите - ДА - возможность частной сети включена.

Есть идеи, почему это зависло?


person keithwarren7    schedule 27.03.2012    source источник
comment
Как вы называете этот async метод? Разве это не вызывает исключение?   -  person svick    schedule 27.03.2012


Ответы (3)


Посмотрите этот ответ на мой вопрос, который кажется очень похожим.

Что-то попробовать: вызовите ConfigureAwait(false) в Задаче, возвращенной GetStreamAsync(). Например.

var result = await httpClient.GetStreamAsync("weeklyplan")
                             .ConfigureAwait(continueOnCapturedContext:false);

Полезно это или нет, зависит от того, как вызывается приведенный выше код - в моем случае вызов метода async с использованием Task.GetAwaiter().GetResult() привел к зависанию кода.

Это потому, что GetResult() блокирует текущий поток до завершения Задачи. Когда задача завершается, она пытается повторно войти в контекст потока, в котором она была запущена, но не может, потому что в этом контексте уже есть поток, который заблокирован вызовом _7 _... deadlock!

В этом сообщении MSDN подробно рассказывается о том, как .NET синхронизирует параллельную темы - и ответ на мой собственный вопрос дает некоторые передовые практики.

person Benjamin Fox    schedule 29.04.2012
comment
Спасибо, почти отказался от async / await, прежде чем увидел это. - person Den; 29.08.2013
comment
Я тоже! Почему это не документировано лучше? Спасибо еще раз - person Avrohom Yisroel; 02.11.2014
comment
Произойдет ли это, если это не произойдет ни в контексте пользовательского интерфейса, ни в контексте ASP.NET? - person machinarium; 24.12.2015
comment
Отличный ответ! Но меня смущает, почему до сих пор у меня эта проблема возникает только при использовании HttpClient, похоже, что базовая реализация в HttpClient не реализована правильно. Другие обходные пути, которые я нашел, включают установку текущего потока как STA, который помогает, но действительно является косвенным, особенно когда вы используете стороннюю сборку и не знаете, что под капотом какой-то вызов будет определенно ждать ответа, что он никогда не получится. В моем случае dll была внутренней, поэтому мы смогли ConfigureAwait ... но это нужно было сделать на самом низком уровне для объекта HttpClient. - person Chris Schaller; 31.05.2016
comment
@ChrisSchaller Обязательно прочитайте полный ответ на странице stackoverflow.com/a/10351400/174735, который довольно полно объясняет проблему . - person Benjamin Fox; 31.05.2016
comment
Это хорошее решение, однако я бы рекомендовал попытаться выяснить, что останавливает метод ожидания, в моем случае это было хост-приложение, которое использовало мой собственный код, который использует async. - person Trevor; 27.04.2017
comment
это отлично сработало для меня с одноразовым классом, который одновременно выполнял кучу вызовов репозитория данных http. держать пари! - person michael g; 19.06.2018
comment
Пожалуйста, прочтите больше о том, что на самом деле делает ConfigureAwait (false), прежде чем использовать его. Он не обязательно предназначен для этой цели, и у него много побочных эффектов. Например, если вы используете это на веб-сайте, у вас не обязательно будет доступ к вещам в HttpContext, потому что вы больше не находитесь в том же контексте. - person CodePB; 20.02.2019
comment
continueOnCapturedContext: false было решением при использовании с веб-сайта ASP.NET - person AlejandroDG; 18.03.2020

Просто предупреждаем - если вы пропустите ожидание на верхнем уровне в контроллере ASP.NET и вернете задачу вместо результата в качестве ответа, на самом деле она просто зависнет во вложенных вызовах ожидания без ошибок. Глупая ошибка, но если бы я увидел этот пост, это могло бы сэкономить мне время, проверяя код на предмет чего-то странного.

person bozzle    schedule 04.07.2019
comment
Не уверен, что вы имеете в виду, это ошибка всякий раз, когда я использую оператор ожидания в неасинхронном методе (метод контроллера). Я тоже не могу изменить его на асинхронный. - person ChiMo; 09.03.2021
comment
можешь показать это на примере? - person Abubakar Riaz; 08.06.2021

Отказ от ответственности: мне не нравится решение ConfigureAwait (), потому что я считаю его неинтуитивным и трудным для запоминания. Вместо этого я пришел к заключению, что вызовы не ожидаемых методов следует переносить в Task.Run (() => myAsyncMethodNotUsingAwait ()). Кажется, это работает на 100%, но может быть просто состоянием гонки !? Я не уверен, что происходит, если честно. Этот вывод может быть неверным, и я рискую своими очками StackOverflow здесь, чтобы, надеюсь, извлечь уроки из комментариев :-P. Пожалуйста, прочтите их!

У меня возникла описанная проблема, и я нашел дополнительную информацию здесь.

Утверждение: «вы не можете вызвать асинхронный метод».

await asyncmethod2()

из метода, который блокирует

myAsyncMethod().Result

В моем случае я не мог изменить метод вызова, и он не был асинхронным. Но на самом деле результат меня не волновал. Насколько я помню, также не сработало удаление .Result и отсутствие ожидания.

Итак, я сделал это:

public void Configure()
{
    var data = "my data";
    Task.Run(() => NotifyApi(data));
}

private async Task NotifyApi(bool data)
{
    var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
    await client.PostAsync("http://...", data);
}

В моем случае меня не волновал результат вызова неасинхронного метода, но я предполагаю, что это довольно часто встречается в этом случае использования. Вы можете использовать результат в вызывающем асинхронном методе.

person CodingYourLife    schedule 02.04.2019