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?

163
demandé sur Krumelur 2014-03-26 00:00:08

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.

162
répondu Reed Copsey 2014-03-25 20:14:01

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:

26
répondu noseratio 2018-05-20 03:08:52

Task.Yield() peut être utilisé dans des implémentations simulées de méthodes asynchrones.

-2
répondu mhsirig 2018-03-11 13:45:05