Ждут работы, но вызов task.Result зависает/deadlocks
У меня есть следующие четыре теста, и последний зависает, когда я запускаю его, поэтому мой вопрос заключается в следующем:
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(@"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
Я использую этот метод расширения для restsharp RestClient:
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
Почему последний тест зависает?
Ответы
Ответ 1
Вы столкнулись с стандартной ситуацией тупика, которую я описываю в своем блоге и в статье MSDN: метод async
пытается запланировать его продолжение в поток, который блокируется вызовом Result
.
В этом случае ваш SynchronizationContext
- это тот, который используется NUnit для выполнения методов тестирования async void
. Вместо этого я попытался бы использовать методы тестирования async Task
.
Ответ 2
Приобретение значения через метод async:
var result = Task.Run(() => asyncGetValue()).Result;
Синхронно вызов метода асинхронного копирования
Task.Run( () => asyncMethod()).Wait();
Никаких проблем с блокировкой не произойдет из-за использования Task.Run.
Ответ 3
Вы можете избежать взаимоблокировки, добавив ConfigureAwait(false)
к этой строке:
IRestResponse<DummyServiceStatus> response = await restResponse;
= >
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
Я описал эту ошибку в своем сообщении в блоге Ловушки async/wait
Ответ 4
Вы блокируете пользовательский интерфейс с помощью свойства Task.Result. В документации MSDN они четко упомянули, что
"Свойство Result является блокирующим свойством. Если вы попытаетесь получить к нему доступ до завершения своей задачи, текущий активный поток блокируется до тех пор, пока задача не завершится и значение не станет доступным. В большинстве случаев вам следует обращаться к значению с помощью Await. или ждите, вместо того, чтобы получить доступ к собственности напрямую. "
Лучшее решение для этого сценария - удалить await и async из методов и использовать только Task, где вы возвращаете результат. Это не испортит вашу последовательность выполнения.
Ответ 5
Если вы не получаете никаких обратных вызовов или элемент управления не зависает, после вызова Асинхронной функции Сервис /API.
Вы должны настроить контекст так, чтобы он возвращал результат в том же вызываемом контексте. Используйте TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Вы столкнетесь с этой проблемой только в веб-приложениях, но не в Static void main