La falta de tarea no capturada. El rendimiento me obliga a usar la tarea. Ejecutar, ¿por qué seguir eso?

Disculpas por adelantado si esta pregunta está basada en la opinión. La falta deTarea. Rendimiento La versión que no capturaría el contexto de ejecución ya fue discutida.aquí. Aparentemente, esta característica estaba presente de alguna forma en las primeras versiones de Async CTP, pero se eliminó porque podría ser mal utilizada fácilmente.

OMI, esta característica podría ser mal utilizada tan fácilmente comoTask.Run sí mismo. Esto es lo que quiero decir. Imagina que hay una esperable.SwitchContext.Yield API que programa la continuación en ThreadPool, por lo que la ejecución siempre continuará en un subproceso diferente del subproceso de llamada. Podría haberlo utilizado en el siguiente código, que inicia un trabajo vinculado a la CPU desde un subproceso de la interfaz de usuario. Lo consideraría una forma conveniente de continuar el trabajo vinculado a la CPU en un subproceso de grupo:

class Worker
{
    static void Log(string format, params object[] args)
    {
        Debug.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, String.Format(format, args));
    }

    public async Task UIAction()
    {
        // UI Thread
        Log("UIAction");

        // start the CPU-bound work
        var cts = new CancellationTokenSource(5000);
        var workTask = DoWorkAsync(cts.Token); 

        // possibly await for some IO-bound work 
        await Task.Delay(1000);
        Log("after Task.Delay");

        // finally, get the result of the CPU-bound work
        int c = await workTask;
        Log("Result: {0}", c);
    }

    async Task<int> DoWorkAsync(CancellationToken ct)
    {
        // start on the UI thread
        Log("DoWorkAsync");

        // switch to a pool thread and yield back to the UI thread
        await SwitchContext.Yield();
        Log("after SwitchContext.Yield");
        // continue on a pool thread

        int c = 0;
        while (!ct.IsCancellationRequested)
        {
            // do some CPU-bound work on a pool thread: counting cycles :)
            c++;
            // and use async/await too
            await Task.Delay(50);
        }

        return c;
    }

}

Ahora, sinSwitchContext.Yield, DoWorkAsync se vería a continuación. Agrega algún nivel adicional de complejidad en forma de delegado asíncrono y anidamiento de tareas:

async Task<int> DoWorkAsync(CancellationToken ct)
{
    // start on the UI thread
    Log("DoWorkAsync");

    // Have to use async delegate
    // Task.Run uwraps the inner Task<int> task
    return await Task.Run(async () =>
    {
        // continue on a pool thread
        Log("after Task.Yield");

        int c = 0;
        while (!ct.IsCancellationRequested)
        {
            // do some CPU-bound work on a pool thread: counting cycles :)
            c++;
            // and use async/await too
            await Task.Delay(50);
        }

        return c;
    });
}

Dicho esto, implementandoSwitchContext.Yield En realidad puede ser bastante simple y (me atrevo a decir) eficiente:

public static class SwitchContext
{
    public static Awaiter Yield() { return new Awaiter(); }

    public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
    {
        public Awaiter GetAwaiter() { return this; }

        public bool IsCompleted { get { return false; } }

        public void OnCompleted(Action continuation)
        {
            ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
        }

        public void GetResult() { }
    }
}

Asi que,mi pregunta es¿Por qué debería preferir la segunda versión deDoWorkAsync sobre el primero, y por qué usarSwitchContext.Yield ser considerado una mala práctica?

Respuestas a la pregunta(1)

Su respuesta a la pregunta