如何在C#中实现计时器的Task Async?

ElH*_*aix 8 .net c# multithreading async-await

我希望给定的操作执行一段时间.当该时间到期时,发送另一个执行命令.

StartDoingStuff();
System.Threading.Thread.Sleep(200);
StopDoingStuff();
Run Code Online (Sandbox Code Playgroud)

而不是在那里有一个阻塞应用程序其余部分的sleep语句,我怎样才能在C#中使用Async/Task/Await来编写它?

Pan*_*vos 10

这个问题在2011年并行团队的博客中由Joe Hoag回答:制作一个Task.TimeoutAfter方法.

该解决方案使用TaskCompletionSource并包含多个优化(12%仅通过避免捕获),处理清理并涵盖边缘情况,如在目标任务已完成时调用TimeoutAfter,传递无效超时等.

Task.TimeoutAfter的美妙之处在于它很容易与其他延续组合,因为它只做一件事:通知你超时已经过期.它并没有'尝试取消你的任务.您可以决定在抛出TimeoutException时要执行的操作.

async/await还介绍了Stephen Toub 使用的快速实现,尽管边缘情况也未被涵盖.

优化的实现是:

public static Task TimeoutAfter(this Task task, int millisecondsTimeout)
{
    // Short-circuit #1: infinite timeout or task already completed
    if (task.IsCompleted || (millisecondsTimeout == Timeout.Infinite))
    {
        // Either the task has already completed or timeout will never occur.
        // No proxy necessary.
        return task;
    }

    // tcs.Task will be returned as a proxy to the caller
    TaskCompletionSource<VoidTypeStruct> tcs = 
        new TaskCompletionSource<VoidTypeStruct>();

    // Short-circuit #2: zero timeout
    if (millisecondsTimeout == 0)
    {
        // We've already timed out.
        tcs.SetException(new TimeoutException());
        return tcs.Task;
    }

    // Set up a timer to complete after the specified timeout period
    Timer timer = new Timer(state => 
    {
        // Recover your state information
        var myTcs = (TaskCompletionSource<VoidTypeStruct>)state;

        // Fault our proxy with a TimeoutException
        myTcs.TrySetException(new TimeoutException()); 
    }, tcs, millisecondsTimeout, Timeout.Infinite);

    // Wire up the logic for what happens when source task completes
    task.ContinueWith((antecedent, state) =>
    {
        // Recover our state data
        var tuple = 
            (Tuple<Timer, TaskCompletionSource<VoidTypeStruct>>)state;

        // Cancel the Timer
        tuple.Item1.Dispose();

        // Marshal results to proxy
        MarshalTaskResults(antecedent, tuple.Item2);
    }, 
    Tuple.Create(timer, tcs),
    CancellationToken.None,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Default);

    return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)

和Stephen Toub的实施,没有检查边缘情况:

public static async Task TimeoutAfter(this Task task, int millisecondsTimeout)
{
    if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
        await task;
    else
        throw new TimeoutException();
}
Run Code Online (Sandbox Code Playgroud)