Risposte:
La migliore pratica è contrassegnare la funzione async void
solo se si tratta di un metodo di fuoco e di dimenticanza, se si desidera attendere, è necessario contrassegnarlo come async Task
.
Nel caso in cui tu voglia ancora aspettare, avvolgilo in questo modo await Task.Run(() => blah())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
await Task.Run(() => blah())
è fuorviante. Questo non attende il completamento della funzione asincrona blah
, attende solo la (banale) creazione dell'attività e continua immediatamente prima del blah()
completamento.
Thread.Sleep
non è asincrono. Questa domanda riguarda l'attesa di una async void
funzione, diciamoasync void blah() { Task.Delay(10000); }
La migliore soluzione è usare async Task
. Dovresti evitare async void
per diversi motivi, uno dei quali è la componibilità.
Se non è possibile ripristinare il metodo Task
(ad esempio, è un gestore eventi), è possibile utilizzare SemaphoreSlim
per avere il segnale del metodo quando sta per uscire. Valuta di farlo in un finally
blocco.
eseguire un AutoResetEvent, chiamare la funzione, quindi attendere AutoResetEvent e quindi impostarlo in un vuoto asincrono quando si sa che è stato fatto.
Puoi anche attendere un'attività che ritorni dal tuo vuoto asincrono
Non è necessario fare nulla manualmente, la await
parola chiave mette in pausa l'esecuzione della funzione fino a quando non viene blah()
restituita.
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
è il tipo di oggetto blah()
restituito
Non puoi davvero await
una void
funzione, quindi LoadBlahBlah()
non puoi esserlovoid
LoadBlahBlah()
che finisca, noblah()
So che questa è una vecchia domanda, ma questo è ancora un problema in cui continuo a camminare, eppure non esiste ancora una soluzione chiara per farlo correttamente quando si utilizza asincrono / attendi in un metodo di firma del vuoto asincrono.
Tuttavia, ho notato che .Wait () funziona correttamente all'interno del metodo void.
e poiché il vuoto asincrono e il vuoto hanno la stessa firma, potrebbe essere necessario eseguire le seguenti operazioni.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Async / wait abbastanza confuso non si blocca sul codice successivo.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
Quando decompili il tuo codice, suppongo che il vuoto asincrono crei un'attività interna (proprio come l'attività asincrona), ma poiché la firma non supporta il ripristino di tali attività interne
ciò significa che internamente il metodo vuoto asincrono sarà ancora in grado di "attendere" metodi interni asincroni. ma esternamente incapace di sapere quando l'attività interna è completa.
Quindi la mia conclusione è che il vuoto asincrono funziona come previsto, e se hai bisogno di feedback dall'attività interna, devi invece usare la firma dell'attività asincrona.
speriamo che il mio divagare abbia senso per chiunque cerchi anche risposte.
Modifica: ho creato un codice di esempio e l'ho decompilato per vedere cosa sta realmente succedendo.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Si trasforma in (modifica: so che il codice del corpo non è qui ma nelle macchine a stati, ma le macchine a stati erano sostanzialmente identiche, quindi non mi sono preoccupato di aggiungerle)
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;
}
né AsyncVoidMethodBuilder o AsyncTaskMethodBuilder in realtà hanno alcun codice nel metodo Start che suggerisca di bloccarli e che verrebbero sempre eseguiti in modo asincrono dopo l'avvio.
cioè senza l'attività di ritorno, non ci sarebbe modo di verificare se è completa.
come previsto, avvia solo l'attività in esecuzione asincrona e quindi continua nel codice. e l'attività asincrona, prima avvia l'attività, quindi la restituisce.
quindi immagino che la mia risposta sarebbe di non usare mai il vuoto asincrono, se hai bisogno di sapere quando l'attività è terminata, ecco a cosa serve l'attività asincrona.