Quand devrais-je utiliser de la Tâche.Rendement()?
J'utilise beaucoup async / await et Task
mais je n'ai jamais utilisé Task.Yield()
et pour être honnête, même avec toutes les explications, Je ne comprends pas pourquoi j'aurais besoin de cette méthode.
Quelqu'un peut-il donner un bon exemple où Yield()
est requis?
3 réponses
Lorsque vous utilisez async
/await
, il n'y a aucune garantie que la méthode que vous appelez lorsque vous ne await FooAsync()
fonctionneront 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 que vous ne bloquiez pas et que vous exécutiez 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 (bloquant efficacement), l'utilisation de await Task.Yield()
forcera votre méthode à être asynchrone, et retournera le contrôle à de ce point. Le reste du code s'exécutera plus tard (à ce moment-là, il peut encore 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'appel Task.Yield()
, la méthode s'exécutera de manière synchrone jusqu'au premier appel à await
.
En interne, await Task.Yield()
met simplement en file d'attente la suite sur le contexte de synchronisation actuel ou sur un thread de pool aléatoire, si SynchronizationContext.Current
est null
.
Il est efficacement implémenté en tant qu'attente personnalisée. Un code moins efficace produisant l'effet identique pourrait être aussi simple que ceci:
var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
if (sc != null)
sc.Post(_ => tcs.SetResult(true), null);
else
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(true));
await tcs.Task;
Task.Yield()
peut être utilisé comme raccourci pour certaines modifications de flux d'exécution étranges. Par exemple:
async Task DoDialogAsync()
{
var dialog = new Form();
Func<Task> showAsync = async () =>
{
await Task.Yield();
dialog.ShowDialog();
}
var dialogTask = showAsync();
await Task.Yield();
// now we're on the dialog's nested message loop started by dialog.ShowDialog
MessageBox.Show("The dialog is visible, click OK to close");
dialog.Close();
await dialogTask;
// we're back to the main message loop
}
Cela dit, Je ne peux penser à aucun cas où Task.Yield()
ne peut pas être remplacé avec Task.Factory.StartNew
w / planificateur de tâches approprié.
Voir aussi:
Task.Yield()
peut être utilisé dans des implémentations simulées de méthodes asynchrones.