Warten Sie auf eine nichtige asynchrone Methode


155

Wie kann ich warten, bis eine void asyncMethode ihren Job beendet hat?

Zum Beispiel habe ich eine Funktion wie unten:

async void LoadBlahBlah()
{
    await blah();
    ...
}

Jetzt möchte ich sicherstellen, dass alles geladen wurde, bevor ich woanders weitermache.

Antworten:


233

Es wird empfohlen, die Funktion async voidnur dann zu markieren, wenn es sich um eine Feuer- und Vergessensmethode handelt. Wenn Sie warten möchten, sollten Sie sie als markieren async Task.

Falls Sie noch warten möchten, wickeln Sie es so ein await Task.Run(() => blah())


blah gibt offensichtlich die Aufgabe zurück und hat eine asynchrone Signatur, warum sollten Sie sie ohne Wartezeit aufrufen? Sogar VS wird Sie davor warnen.
Batmaci

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
Themenfeld

1
await Task.Run(() => blah())ist irreführend. Dies wartet nicht auf den Abschluss der asynchronen Funktion blah, sondern nur auf die (triviale) Erstellung der Aufgabe und wird unmittelbar vor blah()Abschluss fortgesetzt .
Jonathan Lidbeck

@JonathanLidbeck die Anweisung "wird sofort fortgesetzt, bevor blah falsch ist. Versuchen Sie" warte auf Task.Run (() => Thread.Sleep (10_000)) ", die Aufgabe wird 10 Sekunden + gewartet, bevor eine nächste Zeile ausgeführt wird
Rohit Sharma

@RohitSharma, das ist für Ihr Beispiel korrekt, aber Thread.Sleepnicht asynchron. Bei dieser Frage geht es darum, auf eine async voidVeranstaltung zu warten , sagen wirasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck

59

Wenn Sie die Signatur Ihrer Funktion in ändern können, können async TaskSie den hier dargestellten Code verwenden


27

Die beste Lösung ist zu verwenden async Task. Sie sollten async voidaus mehreren Gründen vermeiden , von denen einer die Kompositionsfähigkeit ist.

Wenn die Methode nicht zurückgegeben werden kannTask (z. B. ein Ereignishandler), können Sie SemaphoreSlimdas Methodensignal verwenden, wenn es beendet werden soll. Betrachten Sie dies in einem finallyBlock.


1

Führen Sie ein AutoResetEvent durch, rufen Sie die Funktion auf, warten Sie auf AutoResetEvent und setzen Sie es dann in async void, wenn Sie wissen, dass es fertig ist.

Sie können auch auf eine Aufgabe warten, die von Ihrem ungültigen Async zurückkehrt


1

Sie müssen eigentlich nichts manuell tun, das awaitSchlüsselwort pausiert die Funktionsausführung, bis es blah()zurückkehrt.

private async void SomeFunction()
{
     var x = await LoadBlahBlah(); <- Function is not paused
     //rest of the code get's executed even if LoadBlahBlah() is still executing
}

private async Task<T> LoadBlahBlah()
{
     await DoStuff();  <- function is paused
     await DoMoreStuff();
}

Tist die Art der blah()Objektrückgabe

Sie können nicht wirklich awaiteine voidFunktion sein, also LoadBlahBlah()kann es nicht seinvoid


Ich möchte warten, LoadBlahBlah()bis ich fertig bin , nichtblah()
MBZ

10
Leider pausieren asynchrone void-Methoden die Ausführung nicht (im Gegensatz zu asynchronen Task-Methoden)
ghord

-1

Ich weiß, dass dies eine alte Frage ist, aber dies ist immer noch ein Problem, auf das ich immer wieder stoße, und dennoch gibt es keine klare Lösung, um dies korrekt zu tun, wenn async / await in einer asynchronen Void-Signaturmethode verwendet wird.

Ich habe jedoch festgestellt, dass .Wait () innerhalb der void-Methode ordnungsgemäß funktioniert.

und da async void und void dieselbe Signatur haben, müssen Sie möglicherweise Folgendes tun.

void LoadBlahBlah()
{
    blah().Wait(); //this blocks
}

Verwirrenderweise blockiert async / await den nächsten Code nicht.

async void LoadBlahBlah()
{
    await blah(); //this does not block
}

Wenn Sie Ihren Code dekompilieren, schätze ich, dass async void eine interne Aufgabe erstellt (genau wie asynchrone Aufgabe), aber da die Signatur die Rückgabe dieser internen Aufgaben nicht unterstützt

Dies bedeutet, dass die asynchrone void-Methode intern weiterhin intern asynchrone Methoden "abwarten" kann. aber extern nicht in der Lage zu wissen, wann die interne Aufgabe abgeschlossen ist.

Mein Fazit ist also, dass die asynchrone Leere wie beabsichtigt funktioniert. Wenn Sie Feedback von der internen Aufgabe benötigen, müssen Sie stattdessen die Signatur der asynchronen Aufgabe verwenden.

hoffentlich macht mein Streifzug Sinn für jeden, der auch nach Antworten sucht.

Bearbeiten: Ich habe einen Beispielcode erstellt und ihn dekompiliert, um zu sehen, was tatsächlich vor sich geht.

static async void Test()
{
    await Task.Delay(5000);
}

static async Task TestAsync()
{
    await Task.Delay(5000);
}

Wird zu (bearbeiten: Ich weiß, dass der Body-Code nicht hier, sondern in den Statemaschinen ist, aber die Statemaschinen waren im Grunde identisch, also habe ich mich nicht darum gekümmert, sie hinzuzufügen)

private static void Test()
{
    <Test>d__1 stateMachine = new <Test>d__1();
    stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
    <TestAsync>d__2 stateMachine = new <TestAsync>d__2();
    stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

Weder AsyncVoidMethodBuilder noch AsyncTaskMethodBuilder enthalten tatsächlich Code in der Start-Methode, der darauf hinweist, dass sie blockiert werden sollen, und der nach dem Start immer asynchron ausgeführt wird.

Das heißt, ohne die zurückgebende Aufgabe gibt es keine Möglichkeit zu überprüfen, ob sie abgeschlossen ist.

Wie erwartet wird nur die asynchrone Task gestartet und anschließend im Code fortgesetzt. und die asynchrone Aufgabe, zuerst startet sie die Aufgabe und gibt sie dann zurück.

Ich denke, meine Antwort wäre, niemals asynchrone Leere zu verwenden. Wenn Sie wissen müssen, wann die Aufgabe erledigt ist, ist dies die Aufgabe der asynchronen Aufgabe.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.