Асинхронный вызов с ожиданием в HttpClient никогда не возвращается
У меня есть вызов, который я делаю из встроенного приложения на базе xaml, C#
на 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
игнорируется, и он просто висит там.
Перед тем, как вы спросите - ДА - включена функция Private Network.
Любые идеи, почему это зависает?
Ответы
Ответ 1
Отметьте этот ответ на мой вопрос, который кажется очень похожим.
Что-то попробовать: вызовите ConfigureAwait(false)
в Задаче, возвращенной GetStreamAsync()
. Например.
var result = await httpClient.GetStreamAsync("weeklyplan")
.ConfigureAwait(continueOnCapturedContext:false);
Независимо от того, действительно ли это полезно, зависит от того, как вызывается код выше - в моем случае вызов метода async
с помощью Task.GetAwaiter().GetResult()
заставил код зависать.
Это потому, что GetResult()
блокирует текущий поток до завершения задачи. Когда задача завершается, она пытается повторно ввести контекст потока, в котором он был запущен, но не может, потому что в этом контексте уже есть поток, который блокируется вызовом GetResult()
... deadlock!
Этот пост MSDN подробно описывает, как .NET синхронизирует параллельные потоки - и ответ на мой вопрос дает некоторые лучшие практики.
Ответ 2
Отказ от ответственности: мне не нравится решение 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);
}
В моем случае меня не волновал результат вызова не асинхронного метода, но я думаю, что в этом случае это довольно распространено. Вы можете использовать результат в вызывающем асинхронном методе.
Ответ 3
Просто напутствие - если вы пропустили ожидание на верхнем уровне в контроллере ASP.NET, и вы возвращаете задачу, а не результат в качестве ответа, она фактически просто висит во вложенных вызовах ожидания без ошибок. Глупая ошибка, но если бы я увидел этот пост, он мог бы сэкономить мне время на проверку кода на предмет чего-то странного.