Der wait-Operator wartet nicht wie erwartet

Ich arbeite an einer KlasseDelayedExecutor das verzögert die Ausführung einesAction an sein @ übergebDelayExecute Methode zu einer bestimmten Zeittimeout (siehe Code unten) mit den Anweisungen async und await. Ich möchte auch in der Lage sein, die Ausführung innerhalb des @ abzubrechtimeout Intervall bei Bedarf. Ich habe einen kleinen Test geschrieben, um sein Verhalten wie folgt zu testen:

Code fürTestmethod:

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

Die Ausgabe, die ich von diesem Test erwartet hatte, war, dass sie mir zeigen würde:

sw.ElapsedMilliseconds außerhalb von DelayExecute 1: ...

Ran Action! "

sw.ElapsedMilliseconds in DelayExecute: ...

sw.ElapsedMilliseconds außerhalb von DelayExecute 2:

Jedoch bekomme ich das und verstehe nicht warum:

sw.ElapsedMilliseconds außerhalb von DelayExecute 1: 0

sw.ElapsedMilliseconds außerhalb von DelayExecute 2: 30

Ran Action!

sw.ElapsedMilliseconds inside DelayExecute: 1015

On thisBlogeintra Ich lese

Ich stelle mir "Warten" gerne als "asynchrones Warten" vor. Das heißt, die asynchrone Methode wird angehalten, bis das Erwarten abgeschlossen ist (also wartet sie), aber der eigentliche Thread ist nicht blockiert (also asynchron).

Dies scheint meiner Erwartung zu entsprechen, also was ist hier los und wo ist mein Fehler?

Code für DelayedExecutor:

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

Aktualisieren

Wie vorgeschlagen, habe ich diese Zeile in meinem @ geänderDelayExecute

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

i

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

Damit dies funktioniert, musste ich die Signatur in dieses @ änder

public async Task<Task> DelayExecute(Action func)

so dass meine neue Definition dies ist:

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

Aber jetzt habe ich das gleiche Verhalten wie vorher. Gibt es eine Möglichkeit, mit meinem Design das zu erreichen, was ich versuche? Oder ist es grundlegend fehlerhaft?

Übrigens habe ich auch versucht, @ setzawait await delayTask; in meinem TestDelayExecuteTest, aber das gibt mir den Fehler

Kann nicht 'void' erwarten

Dies ist die aktualisierte TestmethodeDelayExecuteTest, welchenich compile:

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

Antworten auf die Frage(4)

Ihre Antwort auf die Frage