Ich habe die folgenden vier Tests und der letzte hängt, wenn ich ihn ausführe. Warum passiert das:
[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?
async void
Unit-Test-Methoden. Sie werden einfach nicht funktionieren. NUnit tut dies jedoch. Trotzdem stimme ich dem allgemeinen Prinzip des Vorzugsasync Task
vorasync void
.Antworten:
Sie stoßen auf die Standard-Deadlock-Situation, die ich in meinem Blog und in einem MSDN-Artikel beschreibe : Die
async
Methode versucht, ihre Fortsetzung für einen Thread zu planen, der durch den Aufruf von blockiert wirdResult
.In diesem Fall
SynchronizationContext
wird von NUnitasync void
Testmethoden ausgeführt. Ich würdeasync Task
stattdessen versuchen, Testmethoden zu verwenden.quelle
async
ganzen Weg" zu verwenden (wie in meinem MSDN-Artikel angegeben). Mit anderen Worten - wie der Titel meines Blogposts besagt - "Blockiere keinen asynchronen Code".Wait()
durch das Erstellen der aufrufenden Methodeasync
. Aber für mich scheint dies das Problem in den Vordergrund zu rücken. Irgendwann muss etwas synchron verwaltet werden. Was ist, wenn meine Funktion absichtlich synchron ist, weil sie lange laufende Arbeitsthreads mit verwaltetTask.Run()
? Wie warte ich darauf, bis der Vorgang abgeschlossen ist, ohne dass mein NUnit-Test blockiert?At some point, something has to be managed synchronously.
- überhaupt nicht. Bei UI-Apps kann der Einstiegspunkt einasync void
Ereignishandler sein. Bei Server-Apps kann der Einstiegspunkt eineasync Task<T>
Aktion sein. Es ist vorzuziehen,async
für beide zu verwenden, um das Blockieren von Threads zu vermeiden. Ihr NUnit-Test kann synchron oder asynchron sein. Wenn asynchron, machen Sie esasync Task
stattasync void
. Wenn es synchron ist, sollte es keine haben,SynchronizationContext
also sollte es keinen Deadlock geben.Erfassen eines Werts über eine asynchrone Methode:
Synchrones Aufrufen einer asynchronen Methode
Durch die Verwendung von Task.Run treten keine Deadlock-Probleme auf.
quelle
async void
Unit-Test-Methoden zu fördern und Garantien für den gleichen Thread zu entfernen, dieSynchronizationContext
von dem zu testenden System bereitgestellt werden ..GetAwaiter().GetResult()
stattdessen verwenden,.Result
damitException
es nicht verpackt wird.Sie können Deadlocks vermeiden
ConfigureAwait(false)
, die dieser Zeile hinzugefügt werden:=>
Ich habe diese Fallstricke in meinem Blog-Beitrag Fallstricke von Async / Warten beschrieben
quelle
Sie blockieren die Benutzeroberfläche mithilfe der Task.Result-Eigenschaft. In der MSDN-Dokumentation haben sie klar erwähnt, dass
Die beste Lösung für dieses Szenario wäre , sowohl das Warten als auch das Asynchronisieren aus den Methoden zu entfernen und nur die Aufgabe zu verwenden, bei der Sie das Ergebnis zurückgeben. Es wird Ihre Ausführungssequenz nicht durcheinander bringen.
quelle
Wenn Sie keine Rückrufe erhalten oder das Steuerelement auflegt, müssen Sie nach dem Aufrufen der asynchronen Funktion service / API Context so konfigurieren, dass ein Ergebnis für denselben aufgerufenen Kontext zurückgegeben wird.
Verwenden
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Dieses Problem tritt nur in Webanwendungen auf, nicht jedoch in
static void main
.quelle
ConfigureAwait
Vermeidet Deadlocks in bestimmten Szenarien, indem sie nicht im ursprünglichen Thread-Kontext ausgeführt werden.