Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中为计时器实现任务异步?_C#_.net_Multithreading_Async Await - Fatal编程技术网

如何在C#中为计时器实现任务异步?

如何在C#中为计时器实现任务异步?,c#,.net,multithreading,async-await,C#,.net,Multithreading,Async Await,我想要一个给定的操作执行一段时间。该时间到期后,发送另一个执行命令 StartDoingStuff(); System.Threading.Thread.Sleep(200); StopDoingStuff(); 如果StartDoingStuff和StopDoingStuff被创建为返回任务的异步方法,那么如何使用C#?中的Async/Task/Await来编写它,而不是使用阻塞应用程序其余部分的sleep语句 await StartDoingStuff(); await Task.Dela

我想要一个给定的操作执行一段时间。该时间到期后,发送另一个执行命令

StartDoingStuff();
System.Threading.Thread.Sleep(200);
StopDoingStuff();

如果StartDoingStuff和StopDoingStuff被创建为返回任务的异步方法,那么如何使用C#?

中的Async/Task/Await来编写它,而不是使用阻塞应用程序其余部分的sleep语句

await StartDoingStuff();
await Task.Delay(200);
await StopDoingStuff();
编辑: 如果原始提问者想要一个异步方法,该方法将在特定时间后取消:假设该方法不会发出任何网络请求,而只是在内存中进行一些处理,并且结果可以在不考虑其影响的情况下任意中止,则使用取消令牌:

    private async Task Go()
    {
        CancellationTokenSource source = new CancellationTokenSource();
        source.CancelAfter(200);
        await Task.Run(() => DoIt(source.Token));

    }

    private void DoIt(CancellationToken token)
    {
        while (true)
        {
            token.ThrowIfCancellationRequested();
        }
    }

编辑:我应该提到,您可以捕获生成的OperationCanceledException,它提供了任务结束方式的指示,避免了处理bools的需要。

下面是我的方法,使用(不引发异常的选项)

[已编辑]更新为使用Svick的建议通过
CancellationTokenSource设置超时

下面介绍如何从常规方法调用它并异步处理完成:

private void Form1_Load(object sender, EventArgs e)
{
    Task<bool> task = DoSomethingWithTimeoutAsync(3000);
    task.ContinueWith(_ =>
    {
        MessageBox.Show("DoSomethingWithTimeout done:" + task.Result); // false is cancelled
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
private void Form1\u加载(对象发送方,事件参数e)
{
Task Task=DoSomethingWithTimeoutAsync(3000);
task.ContinueWith(=>
{
MessageBox.Show(“DoSomethingWithTimeout done:+task.Result);//取消false
},TaskScheduler.FromCurrentSynchronizationContext());
}

2011年,Joe Hoag在Parallel团队的博客中回答了这个问题:

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

Task.TimeoutAfter的美妙之处在于,它很容易与其他continuation组合在一起,因为它只做一件事:通知您超时已过期。它不会试图取消你的任务。您可以决定在抛出TimeoutException时执行什么操作

Stephen Toub还介绍了一个使用
async/await
的快速实现,尽管没有介绍边缘情况

优化的实现是:

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

我很困惑,你问的是
async
-
await
,这是C#5.0的一个新特性,但你的问题被标记为C#4.0。那么,它是哪一个?切换标签以匹配question@ElHaix其他人似乎建议不要抛出异常。抛出异常是正常的,因为代码将更加优雅、可维护,并且是实现“使用ThrowIfCancellationRequested方法。以这种方式取消的任务将转换为取消状态,调用代码可以使用该状态验证任务是否响应了其取消请求。“假设您有多个使用同一取消令牌的方法。使用异常简化所有逻辑。我在这里重复我的评论。虽然这看起来是一件合乎逻辑的事情,但对于持续200毫秒的任务来说,抛出取消异常可能会相对昂贵,尤其是当此代码像Start/Stop/Start/Stop一样重复时。@ElHaix,您是否希望对可以恢复的任务执行此操作,如
startdoingstaff();睡眠(200);停止做某事();睡眠(200);ResumeDoingStuff();/*等等*/
?为什么异步等待要求这两个方法也是
async
?我在重读这个问题后修改了这个方法。谢谢。忙着等待代币似乎没有什么目的。如果你是指while(真的)…这是一个例子。对于持续时间只有200毫秒的东西,抛出取消例外相对比较昂贵,特别是如果此代码重复执行,如Do/Stop/Do/Stop…如果您想创建一个在.Net 4.5上一段时间后自动取消的
CancellationToken
,那么就有了.Good point@svick,我已经更新了代码以使用此功能。您应该抛出异常。从您自己的“任务取消模式”链接:“执行此操作的首选方法是使用ThrowIfCancellationRequested方法。以这种方式取消的任务将转换为已取消状态”@Jayce,我可以选择,因为工作任务完全封装在
DoSomethingWithTimeoutAsync
中,为了简单起见,我选择不使用异常。
private void Form1_Load(object sender, EventArgs e)
{
    Task<bool> task = DoSomethingWithTimeoutAsync(3000);
    task.ContinueWith(_ =>
    {
        MessageBox.Show("DoSomethingWithTimeout done:" + task.Result); // false is cancelled
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
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;
}
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();
}