Réponses:
La meilleure pratique consiste à ne marquer la fonction async void
que 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())
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
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.
Thread.Sleep
n'est pas asynchrone. Cette question concerne l'attente d'une async void
fonction, disonsasync void blah() { Task.Delay(10000); }
Si vous pouvez changer la signature de votre fonction en async Task
alors vous pouvez utiliser le code présenté ici
La meilleure solution est d'utiliser async Task
. Vous devez éviter async void
pour 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 SemaphoreSlim
pour que la méthode signale qu'elle est sur le point de se terminer. Pensez à faire cela dans un finally
bloc.
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
Vous n'avez pas vraiment besoin de faire quoi que ce soit manuellement, le await
mot - 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();
}
T
est le type d'objet blah()
renvoyé
Vous ne pouvez pas vraiment await
une void
fonction donc LoadBlahBlah()
ne peut pas êtrevoid
LoadBlahBlah()
finir, pasblah()
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.