如何创建可取消的任务循环?

6

是否可以使用System.Threading.Task.Task创建可以取消的任务循环?

流程应该从Task.Delay(x ms)开始,然后继续进行用户定义的任务,然后再次执行Task.Delay(y ms),并从用户定义的任务重复执行。

var result = Task.Delay(initialDelay)
              .ContinueWith(t => dostuff..)
              .ContinueWith what goes here?

使用任务能实现吗?

我可以启动一个计时器并完成它,但如果需要取消操作,使用任务似乎是正确的选择,不是吗?

1个回答

10

await 可以让这个过程变得非常简单:

public async Task TimedLoop(Action action, 
    CancellationToken token, TimeSpan delay)
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        action();
        await Task.Delay(delay, token);
    }
}

没有使用async(但仍然只使用TPL),这会有点混乱。我通常通过使用连接到Task类型变量的继续来解决这个问题。这样做没问题,但可能需要一点时间来理解它。如果不使用await,那么使用Timer可能更容易。

public Task TimedLoop(Action action,
    CancellationToken token, TimeSpan delay)
{
    //You can omit these two lines if you want the method to be void.
    var tcs = new TaskCompletionSource<bool>();
    token.Register(() => tcs.SetCanceled());

    Task previous = Task.FromResult(true);
    Action<Task> continuation = null;
    continuation = t =>
    {
        previous = previous.ContinueWith(t2 => action(), token)
            .ContinueWith(t2 => Task.Delay(delay, token), token)
            .Unwrap()
            .ContinueWith(t2 => previous.ContinueWith(continuation, token));
    };
    previous.ContinueWith(continuation, token);
    return tcs.Task;
}

1
我也会将取消令牌传递给“Delay”方法。 - Eli Arbel
似乎是滥用迭代器模拟异步的一个很好的使用案例。有一个库可以做到这一点。 - usr
@Noseratio 多人已经对这个想法进行了研究:https://www.google.com/webhp?complete=1&hl=en#complete=1&hl=en&q=.net+async+iterator 我也私下实现了这样的东西。 - usr
@usr,很好地使用了ContinueWith/Unwrap,尽管在.NET 4.0中没有Task.FromResult。最接近的可能是使用即时SetResultTaskCompletionSource - noseratio - open to work
用户取消操作应该返回什么对象?CancellationTokenSource? - Roger Johansson
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接