Attendez une méthode asynchrone vide

155

Comment puis-je attendre qu'une void asyncméthode termine son travail?

par exemple, j'ai une fonction comme ci-dessous:

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

maintenant je veux m'assurer que tout a été chargé avant de continuer ailleurs.

MBZ
la source

Réponses:

234

La meilleure pratique consiste à ne marquer la fonction async voidque s'il s'agit de la méthode fire and forget, si vous voulez attendre, vous devez la marquer comme async Task.

Au cas où vous voudriez toujours attendre, enveloppez-le comme ça await Task.Run(() => blah())

Rohit Sharma
la source
blah renvoie évidemment la tâche et a la signature asynchrone pourquoi l'appelleriez-vous sans attendre. même VS vous en avertira.
batmaci
8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
themefield
1
await Task.Run(() => blah())est trompeur. Cela n'attend pas la fin de la fonction asynchrone blah, il attend juste la création (triviale) de la tâche et se poursuit immédiatement avant la blah()fin.
Jonathan Lidbeck le
@JonathanLidbeck l'instruction "continue immédiatement avant que blah ne soit faux. Essayez" wait Task.Run (() => Thread.Sleep (10_000)) ", la tâche est attendue pendant 10 secondes + avant d'exécuter toute ligne suivante
Rohit Sharma
@RohitSharma, c'est correct pour votre exemple, mais Thread.Sleepn'est pas asynchrone. Cette question concerne l'attente d'une async voidfonction, disonsasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck
59

Si vous pouvez changer la signature de votre fonction en async Taskalors vous pouvez utiliser le code présenté ici

Communauté
la source
27

La meilleure solution est d'utiliser async Task. Vous devez éviter async voidpour plusieurs raisons, dont la composabilité.

Si la méthode ne peut pas être renvoyée Task(par exemple, c'est un gestionnaire d'événements), vous pouvez utiliser SemaphoreSlimpour que la méthode signale qu'elle est sur le point de se terminer. Pensez à faire cela dans un finallybloc.

Stephen Cleary
la source
1

faites un AutoResetEvent, appelez la fonction puis attendez AutoResetEvent, puis définissez-le dans async void lorsque vous savez que c'est fait.

Vous pouvez également attendre une tâche qui revient de votre asynchrone void

utilisateur2712345
la source
1

Vous n'avez pas vraiment besoin de faire quoi que ce soit manuellement, le awaitmot - clé interrompt l'exécution de la fonction jusqu'au blah()retour.

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();
}

Test le type d'objet blah()renvoyé

Vous ne pouvez pas vraiment awaitune voidfonction donc LoadBlahBlah()ne peut pas êtrevoid

Mayank
la source
Je veux attendre pour LoadBlahBlah()finir, pasblah()
MBZ
10
Malheureusement, les méthodes async void ne
suspendent
-1

Je sais que c'est une vieille question, mais c'est toujours un problème dans lequel je continue à marcher, et pourtant il n'y a toujours pas de solution claire pour le faire correctement lorsque vous utilisez async / await dans une méthode de signature async void.

Cependant, j'ai remarqué que .Wait () fonctionne correctement dans la méthode void.

et comme async void et void ont la même signature, vous devrez peut-être faire ce qui suit.

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

Async / await assez confus ne bloque pas sur le code suivant.

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

Lorsque vous décompilez votre code, je suppose que async void crée une tâche interne (tout comme une tâche async), mais puisque la signature ne prend pas en charge le retour de ces tâches internes

cela signifie qu'en interne la méthode async void pourra toujours "attendre" les méthodes asynchrones en interne. mais extérieurement incapable de savoir quand la tâche interne est terminée.

Ma conclusion est donc qu'async void fonctionne comme prévu, et si vous avez besoin de commentaires de la tâche interne, vous devez utiliser la signature de tâche async à la place.

j'espère que ma randonnée a du sens pour quiconque cherche également des réponses.

Edit: J'ai créé un exemple de code et je l'ai décompilé pour voir ce qui se passe réellement.

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

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

Se transforme en (modifier: je sais que le code du corps n'est pas ici mais dans les statemachines, mais les statemachines étaient fondamentalement identiques, donc je n'ai pas pris la peine de les ajouter)

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;
}

Ni AsyncVoidMethodBuilder ni AsyncTaskMethodBuilder n'ont en fait de code dans la méthode Start qui leur indiquerait de les bloquer, et s'exécuteraient toujours de manière asynchrone après leur démarrage.

ce qui signifie sans la tâche de retour, il n'y aurait aucun moyen de vérifier si elle est terminée.

comme prévu, il démarre uniquement la tâche en cours d'exécution asynchrone, puis continue dans le code. et la tâche asynchrone, commence d'abord la tâche, puis la renvoie.

donc je suppose que ma réponse serait de ne jamais utiliser async void, si vous avez besoin de savoir quand la tâche est terminée, c'est à cela que sert async Task.

Droa
la source