Асинхронный вызов с ожиданием в 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, и вы возвращаете задачу, а не результат в качестве ответа, она фактически просто висит во вложенных вызовах ожидания без ошибок. Глупая ошибка, но если бы я увидел этот пост, он мог бы сэкономить мне время на проверку кода на предмет чего-то странного.