¿Cuándo usaría Tarea?Yield()?


Estoy usando async/await y Task mucho, pero nunca he estado usando Task.Yield() y para ser honesto, incluso con todas las explicaciones, no entiendo por qué necesitaría este método.

¿Puede alguien dar un buen ejemplo donde se requiere Yield()?

Author: Krumelur, 2014-03-26

3 answers

Cuando se usa async/await, no hay garantía de que el método que llame cuando lo haga await FooAsync() se ejecute de forma asincrónica. La implementación interna es libre de regresar usando una ruta completamente sincrónica.

Si está haciendo una API donde es crítico que no bloquee y ejecute algún código de forma asincrónica, y existe la posibilidad de que el método llamado se ejecute de forma sincrónica (bloqueo efectivo), usando await Task.Yield() forzará a su método a ser asincrónico, y devolverá el control a ese punto. El resto del código se ejecutará en un momento posterior (momento en el que todavía puede ejecutarse sincrónicamente) en el contexto actual.

Esto también puede ser útil si hace un método asíncrono que requiere alguna inicialización "de larga duración", es decir:

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

Sin la llamada Task.Yield(), el método se ejecutará sincrónicamente hasta la primera llamada a await.

 162
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-25 20:14:01

Internamente, await Task.Yield() simplemente pone en cola la continuación en el contexto de sincronización actual o en un subproceso de grupo aleatorio, si SynchronizationContext.Current es null.

Es implementado eficientemente como awaiter personalizado. Un código menos eficiente que produce el efecto idéntico podría ser tan simple como esto:

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() se puede utilizar como un atajo para algunas alteraciones extrañas del flujo de ejecución. Por ejemplo:

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  
}

Dicho esto, no puedo pensar en ningún caso donde Task.Yield() no pueda ser reemplazado con Task.Factory.StartNew w/ programador de tareas adecuado.

Véase también:

 26
Author: noseratio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-20 03:08:52

Task.Yield() puede ser usado en implementaciones simuladas de métodos asincrónicos.

 -2
Author: mhsirig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-11 13:45:05