Что происходит при ожидании результата задачи?
Я использую HttpClient для отправки данных на удаленную службу в проекте .NET 4.0. Я не беспокоюсь об этой блокировке операции, поэтому я решил, что могу пропустить ContinueWith или async/await и использовать Result.
Во время отладки я столкнулся с проблемой, когда удаленный сервер не реагировал. Когда я прошел через код, мне показалось, что мой код просто перестает работать в третьей строке... текущая строка указателя стека перестает выделяться желтым цветом и не переходит к следующей строке. Он просто исчез. Мне потребовалось некоторое время, чтобы понять, что я должен дождаться запроса на тайм-аут.
var client = new HttpClient();
var task = client.PostAsync("http://someservice/", someContent);
var response = task.Result;
Мое понимание заключалось в том, что вызов результата в Задаче заставил код выполнять синхронно, чтобы вести себя как это (я знаю, что в HttpClient нет метода Post):
var client = new HttpClient();
var response = client.Post("http://someservice/", someContent);
Я не уверен, что это плохо, я просто пытаюсь обойти это. Это правда, что в силу того факта, что HttpClient возвращает результаты непосредственно вместо результатов, мое приложение автоматически использует асинхронность, даже когда я думаю, что избегаю этого?
Ответы
Ответ 1
В Windows все операции ввода/вывода являются асинхронными. Синхронные API - это просто абстракция.
Итак, когда вы используете HttpWebRequest.GetResponse
, на самом деле происходит запуск I/O (асинхронно), а вызывающий поток (синхронно) блокирует его до завершения.
Аналогично, когда вы используете HttpClient.PostAsync(..).Result
, ввод/вывод запускается (асинхронно), а вызывающий поток (синхронно) блокирует его до завершения.
Я обычно рекомендую, чтобы люди использовали await
, а не Task.Result
или Task.Wait
по следующим причинам:
- Если вы заблокируете
Task
, который является результатом метода async
, вы можете легко войти в ситуацию взаимоблокировки.
-
Task.Result
и Task.Wait
завершают любые исключения в AggregateException
(поскольку эти API-интерфейсы являются задержками с TPL). Поэтому обработка ошибок сложнее.
Однако, если вы знаете об этих ограничениях, есть ситуации, когда блокировка на Task
может быть полезна (например, в приложении консоли Main
).
Ответ 2
Захват результата задачи блокирует текущий поток. В этом случае нет смысла использовать асинхронную версию метода. Post()
и PostAsync().Result
будут блокированы.
Если вы хотите использовать concurrency, вы должны записать его как таковой:
async Task PostContent()
{
var client = new HttpClient();
Task t = await client.PostAsync("http://someservice/", someContent);
//code after this line will execute when the PostAsync completes.
return t;
}
Так как PostContent()
сам возвращает задачу, метод, вызывающий ее, также должен ждать.
async void ProcessResult()
{
var result = await PostContent();
//Do work with the result when the result is ready
}
Например, если вы вызываете ProcessResult()
в обработчик нажатия кнопки, вы видите, что пользовательский интерфейс все еще реагирует, другие элементы управления все еще функционируют.