web-dev-qa-db-de.com

abwarten funktioniert, aber aufrufende Task. Ergebnis hängt / Deadlocks

Ich habe die folgenden vier Tests und der letzte hängt, wenn ich ihn ausführe. Meine Frage ist, warum dies passiert:

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

Ich benutze diese Erweiterungsmethode für 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; }
}

Warum hängt der letzte Test?

113
Johan Larsson

Sie stoßen auf die von mir beschriebene Standard-Deadlock-Situation in meinem Blog und in einem MSDN-Artikel : Die Methode async versucht, die Fortsetzung zu planen Ein Thread, der durch den Aufruf von Result blockiert wird.

In diesem Fall wird Ihr SynchronizationContext von NUnit verwendet, um async void - Testmethoden auszuführen. Ich würde versuchen, stattdessen async Task - Testmethoden zu verwenden.

80
Stephen Cleary

Erfassen eines Werts über eine asynchrone Methode:

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

Eine asynchrone Methode synchron aufrufen

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

Aufgrund der Verwendung von Task.Run treten keine Deadlock-Probleme auf.

198

Sie können einen Deadlock vermeiden, indem Sie dieser Zeile ConfigureAwait(false) hinzufügen:

IRestResponse<DummyServiceStatus> response = await restResponse;

=>

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

Ich habe diese Fallstricke in meinem Blog-Beitrag beschrieben Fallstricke von async/await

16
Vladimir

Sie blockieren die Benutzeroberfläche mithilfe der Task.Result-Eigenschaft. In MSDN Documentation haben sie klar erwähnt, dass

"Die Ergebnis - Eigenschaft ist eine blockierende Eigenschaft. Wenn Sie versuchen, auf sie zuzugreifen, bevor ihre Aufgabe abgeschlossen ist, wird der derzeit aktive Thread blockiert, bis die Aufgabe abgeschlossen ist und der Wert verfügbar ist. In den meisten Fällen , Sie sollten auf den Wert zugreifen, indem Sie Await oder wait verwenden, anstatt direkt auf die Eigenschaft zuzugreifen. "

Die beste Lösung für dieses Szenario wäre, sowohl wait & async von den Methoden zu entfernen und nur Task zu verwenden, wo Sie das Ergebnis zurückgeben. Es wird Ihre Ausführungsreihenfolge nicht durcheinander bringen.

8
Dark Knight

Wenn Sie nach dem Aufrufen der Service/API-Async-Funktion keine Rückrufe erhalten oder das Steuerelement nicht mehr reagiert.

Sie müssen Context so konfigurieren, dass er das Ergebnis im selben Kontext zurückgibt. Benutze TestAsync().ConfigureAwait(continueOnCapturedContext: false);

Dieses Problem tritt nur in Webanwendungen auf, nicht jedoch in Static void main

2
Mayank Pandit