Attendere un metodo asincrono vuoto


155

Come posso aspettare che un void asyncmetodo finisca il suo lavoro?

ad esempio, ho una funzione come di seguito:

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

ora voglio assicurarmi che tutto sia stato caricato prima di continuare altrove.

Risposte:


234

La migliore pratica è contrassegnare la funzione async voidsolo 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())


blah sta ovviamente restituendo un compito e ha una firma asincrona perché dovresti chiamarlo senza aspettare. anche VS ti avvertirà.
Batmaci,

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

1
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.
Jonathan Lidbeck,

@JonathanLidbeck l'affermazione "continua immediatamente prima che blah sia sbagliato. Prova" waitit Task.Run (() => Thread.Sleep (10_000)) ", l'attività è attesa per 10 secondi + prima di eseguire qualsiasi riga successiva
Rohit Sharma

@RohitSharma, è corretto per il tuo esempio, ma Thread.Sleepnon è asincrono. Questa domanda riguarda l'attesa di una async voidfunzione, diciamoasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck,

59

Se puoi modificare la firma della tua funzione in async Taskallora puoi usare il codice presentato qui


27

La migliore soluzione è usare async Task. Dovresti evitare async voidper diversi motivi, uno dei quali è la componibilità.

Se non è possibile ripristinare il metodo Task(ad esempio, è un gestore eventi), è possibile utilizzare SemaphoreSlimper avere il segnale del metodo quando sta per uscire. Valuta di farlo in un finallyblocco.


1

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


1

Non è necessario fare nulla manualmente, la awaitparola 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 awaituna voidfunzione, quindi LoadBlahBlah()non puoi esserlovoid


Voglio aspettare LoadBlahBlah()che finisca, noblah()
MBZ il

10
Sfortunatamente i metodi void asincroni non mettono in pausa l'esecuzione (diversamente dai metodi Task asincroni)
ghord

-1

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.