Lorsque vous utilisez async
/ await
, il n'y a aucune garantie que la méthode que vous appelez lorsque vous le await FooAsync()
ferez s'exécutera réellement de manière asynchrone. L'implémentation interne est libre de revenir en utilisant un chemin complètement synchrone.
Si vous créez une API où il est essentiel de ne pas bloquer et d'exécuter du code de manière asynchrone, et qu'il y a une chance que la méthode appelée s'exécute de manière synchrone (blocage efficace), l'utilisation await Task.Yield()
force votre méthode à être asynchrone et retourne contrôle à ce point. Le reste du code s'exécutera ultérieurement (à ce moment, il peut toujours s'exécuter de manière synchrone) sur le contexte actuel.
Cela peut également être utile si vous créez une méthode asynchrone qui nécessite une initialisation «longue», c'est-à-dire:
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Sans l' Task.Yield()
appel, la méthode s'exécutera de manière synchrone jusqu'au premier appel à await
.
await Task.Yield()
force la méthode à être asynchrone, pourquoi prendrions-nous la peine d'écrire du "vrai" code asynchrone? Imaginez une méthode de synchronisation lourde. Pour le rendre asynchrone, il suffit d'ajouterasync
etawait Task.Yield()
au début et comme par magie, ce sera asynchrone? Ce serait un peu comme encapsuler tout le code de synchronisationTask.Run()
et créer une fausse méthode asynchrone.Task.Run
pour l'implémenter,ExecuteFooOnUIThread
s'exécutera sur le pool de threads, pas sur le thread d'interface utilisateur. Avecawait Task.Yield()
, vous le forcez à être asynchrone de manière à ce que le code suivant soit toujours exécuté sur le contexte actuel (juste à un moment ultérieur). Ce n'est pas quelque chose que vous feriez normalement, mais c'est bien qu'il y ait l'option si c'est nécessaire pour une raison étrange.ExecuteFooOnUIThread()
était très longue, elle bloquerait encore le thread d'interface utilisateur pendant un certain temps et rendrait l'interface utilisateur non réactive, est-ce correct?En interne, met
await Task.Yield()
simplement la suite en file d'attente sur le contexte de synchronisation actuel ou sur un thread de pool aléatoire, si telSynchronizationContext.Current
est le casnull
.Il est efficacement mis en œuvre comme serveur personnalisé. Un code moins efficace produisant l'effet identique pourrait être aussi simple que cela:
Task.Yield()
peut être utilisé comme raccourci pour certaines modifications de flux d'exécution étranges. Par exemple:Cela dit, je ne peux penser à aucun cas où
Task.Yield()
ne peut pas être remplacé par unTask.Factory.StartNew
bon planificateur de tâches.Voir également:
"wait Task.Yield ()" et ses alternatives
Task.Yield - usages réels?
la source
var dialogTask = await showAsync();
?var dialogTask = await showAsync()
ne compilera pas car l'await showAsync()
expression ne renvoie pas deTask
(contrairement à ce qui se passe sansawait
). Cela dit, si vous le faitesawait showAsync()
, l'exécution ne reprendra qu'après la fermeture de la boîte de dialogue, c'est différent. C'est parce quewindow.ShowDialog
c'est une API synchrone (malgré qu'elle pompe toujours des messages). Dans ce code, je voulais continuer pendant que la boîte de dialogue est toujours affichée.Une utilisation de
Task.Yield()
est d'empêcher un débordement de pile lors de la récursion asynchrone.Task.Yield()
empêche la poursuite syncrone. Notez cependant que cela peut provoquer une exception OutOfMemory (comme l'a noté Triynko). La récursion sans fin n'est toujours pas sûre et vous feriez probablement mieux de réécrire la récursivité en boucle.la source
await Task.Delay(1)
soit suffisant pour l'empêcher. (Application console, .NET Core 3.1, C # 8)Task.Yield()
peut être utilisé dans des implémentations simulées de méthodes asynchrones.la source