Antworten:
Es wird empfohlen, die Funktion async void
nur 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())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
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 .
Thread.Sleep
nicht asynchron. Bei dieser Frage geht es darum, auf eine async void
Veranstaltung zu warten , sagen wirasync void blah() { Task.Delay(10000); }
Wenn Sie die Signatur Ihrer Funktion in ändern können, können async Task
Sie den hier dargestellten Code verwenden
Die beste Lösung ist zu verwenden async Task
. Sie sollten async void
aus 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 SemaphoreSlim
das Methodensignal verwenden, wenn es beendet werden soll. Betrachten Sie dies in einem finally
Block.
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
Sie müssen eigentlich nichts manuell tun, das await
Schlü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();
}
T
ist die Art der blah()
Objektrückgabe
Sie können nicht wirklich await
eine void
Funktion sein, also LoadBlahBlah()
kann es nicht seinvoid
LoadBlahBlah()
bis ich fertig bin , nichtblah()
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.