жду работы, но вызов задачи.Результат зависает / взаимоблокировки


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

[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;
}

Я использую этот метод расширения для RestClient restsharp:

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; }
}

почему последний тест висит?

5 102

5 ответов:

вы сталкиваетесь со стандартной тупиковой ситуацией, которую я описываю мой блог и в статье MSDN: the async метод пытается запланировать его продолжение на поток, который блокируется вызовом Result.

в этом случае SynchronizationContext используется NUnit для выполнения async void методы испытаний. Я бы попробовал использовать методы испытаний.

получение значения с помощью асинхронного метода:

var result = Task.Run(() => asyncGetValue()).Result;

синхронный вызов асинхронного метода

Task.Run( () => asyncMethod()).Wait();

из-за использования задачи не возникнет проблем с взаимоблокировкой.Бежать.

вы можете избежать тупикового добавления ConfigureAwait(false) на эту строку:

IRestResponse<DummyServiceStatus> response = await restResponse;

=>

IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);

Я описал эту ловушку в своем блоге подводные камни асинхронных/ждут

вы блокируете пользовательский интерфейс с помощью задачи.Свойство result. В Примечание они ясно упомянули об этом,

" The результат свойство является блокирующим свойством. Если вы попытаетесь получить к нему доступ прежде чем его задача будет завершена, поток, который в настоящее время активен блокируется до завершения задачи и значение. В большинстве случаи, вы должны получить доступ к значению с помощью ждут или ждут вместо доступ к собственности напрямую."

лучшим решением для этого сценария было бы удалить оба await & async из методов и использовать только задание где вы возвращаете результат. Это не испортит вашу последовательность выполнения.

Если вы не получаете обратных вызовов или управления зависает, после вызова службы / API асинхронной функции. Вы должны настроить контекст, чтобы вернуть результат на том же самом вызванном контексте. Используйте TestAsync ().ConfigureAwait (continueOnCapturedContext: false);

вы будете сталкиваться с этой проблемой только в веб-приложениях, но не в Static void main