El operador espera no está esperando como esperaba

Estoy trabajando en una claseDelayedExecutor eso retrasará la ejecución de unAction pasado a suDelayExecute método por un tiempo determinadotimeout (vea el código a continuación) usando las declaraciones async y wait. También quiero poder abortar la ejecución dentro deltimeout intervalo si es necesario. He escrito una pequeña prueba para probar su comportamiento de esta manera:

Código paraMétodo de prueba:

    [TestMethod]
    public async Task DelayExecuteTest()
    {
        int timeout = 1000;
        var delayExecutor = new DelayedExecutor(timeout);
        Action func = new Action(() => Debug.WriteLine("Ran function!"));
        var sw = Stopwatch.StartNew();
        Debug.WriteLine("sw.ElapsedMilliseconds 1: " + sw.ElapsedMilliseconds);
        Task delayTask = delayExecutor.DelayExecute(func);
        await delayTask;
        Debug.WriteLine("sw.ElapsedMilliseconds 2: " + sw.ElapsedMilliseconds);
        Thread.Sleep(1000);
    }
}

El resultado que esperaba de esta prueba fue que me mostraría:

sw.ElapsedMilliseconds fuera de DelayExecute 1: ...

Ran Action! "

sw.ElapsedMilliseconds dentro de DelayExecute: ...

sw.ElapsedMilliseconds fuera de DelayExecute 2:

Sin embargo, entiendo esto y no entiendo por qué:

sw.ElapsedMilliseconds fuera de DelayExecute 1: 0

sw.ElapsedMilliseconds fuera de DelayExecute 2: 30

Ran Action!

sw.ElapsedMilliseconds dentro de DelayExecute: 1015

En esteentrada en el blog Yo leo:

Me gusta pensar en "esperar" como una "espera asincrónica". Es decir, el método asíncrono se detiene hasta que se completa lo esperado (por lo que espera), pero el subproceso real no está bloqueado (por lo que es asíncrono).

Esto parece estar en línea con mis expectativas, entonces, ¿qué está pasando aquí y dónde está mi error?

Código paraEjecutor retrasado:

public class DelayedExecutor
{
    private int timeout;

    private Task currentTask;
    private CancellationToken cancellationToken;
    private CancellationTokenSource tokenSource;

    public DelayedExecutor(int timeout)
    {
        this.timeout = timeout;
        tokenSource = new CancellationTokenSource();
    }

    public void AbortCurrentTask()
    {
        if (currentTask != null)
        {
            if (!currentTask.IsCompleted)
            {
                tokenSource.Cancel();
            }
        }
    }

    public Task DelayExecute(Action func)
    {
        AbortCurrentTask();

        tokenSource = new CancellationTokenSource();
        cancellationToken = tokenSource.Token;

        return currentTask = Task.Factory.StartNew(async () =>
            {
                var sw = Stopwatch.StartNew();
                await Task.Delay(timeout, cancellationToken);
                func();
                Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
            });
    }
}

Actualizar:

Como sugerí, modifiqué esta línea dentro de miDelayExecute

return currentTask = Task.Factory.StartNew(async () =>

dentro

return currentTask = await Task.Factory.StartNew(async () =>

Para que esto funcione, necesitaba cambiar la firma a esto

public async Task<Task> DelayExecute(Action func)

para que mi nueva definición sea esta:

public async Task<Task> DelayExecute(Action func)
{
    AbortCurrentTask();

    tokenSource = new CancellationTokenSource();
    cancellationToken = tokenSource.Token;

    return currentTask = await Task.Factory.StartNew(async () =>
        {
            var sw = Stopwatch.StartNew();
            await Task.Delay(timeout, cancellationToken);
            func();
            Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
        });
}

Sin embargo, ahora tengo el mismo comportamiento que antes. ¿Hay alguna forma de lograr lo que estoy tratando de hacer con mi diseño? ¿O es fundamentalmente defectuoso?

Por cierto, también intenté ponerawait await delayTask; dentro de mi pruebaDelayExecuteTest, pero esto me da el error

No puedo esperar 'vacío'

Este es el método de prueba actualizadoDelayExecuteTest, cualno compilar:

public async Task DelayExecuteTest()
{
    int timeout = 1000;
    var delayExecutor = new DelayedExecutor(timeout);
    Action func = new Action(() => Debug.WriteLine("Ran Action!"));
    var sw = Stopwatch.StartNew();
    Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
    Task delayTask = delayExecutor.DelayExecute(func);
    await await delayTask;
    Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
    Thread.Sleep(1000);
}

Respuestas a la pregunta(2)

Su respuesta a la pregunta