CancelTokenSource 在给定超时后不会取消任务

本文关键字:取消 任务 超时 CancelTokenSource | 更新日期: 2023-09-27 18:34:14

我正在学习TAP,我喜欢使用单元测试来探索TPL数据流。我有以下一个我无法理解:

var cts = new CancellationTokenSource(500);
var tcs = new TaskCompletionSource<bool>(cts.Token);
var agent = new ActionBlock<FakeMessage>( async evt =>
{
    await Task.Delay(5000);
    tcs.SetResult(true);
});
agent.Post(new FakeMessage());
try
{
    var result = await tcs.Task;
    Assert.Fail();
}
catch (OperationCanceledException ex)
{
    Assert.IsTrue(true);
}
catch (Exception e)
{
    Assert.Fail();
}

我期待它会提高第一行定义的超时并捕获OperationCanceledException,但我总是以Assert.Fail结束 await tcs.Task.有人可以解释一下我的假设有什么不顺利的吗?

CancelTokenSource 在给定超时后不会取消任务

TaskCompletionSource不接受

CancellationToken

它确实接受Object状态,从技术上讲,您可以将CancellationToken传递到其中,但它不会执行任何操作,尤其是不会取消TaskCompletionSource

如果要取消TaskCompletionSource可以通过简单的超时来实现:

Task.Delay(500).ContinueWith(t => tcs.SetCancelled());

您还可以创建一个接受CancellationToken并在取消TaskCompletionSource时自行取消的TaskCompletionSource

class TaskCompletionSourceWithCancellation<T> : TaskCompletionSource<T>
{
    public CancellationToken CancellationToken { get; }
    public TaskCompletionSourceWithCancellation(CancellationToken cancellationToken)
    {
        CancellationToken = cancellationToken;
        var cancellationTokenRegistration =
            cancellationToken.Register(
                _ => ((TaskCompletionSourceWithCancellation<TResult>)_).TrySetCanceled(),
                this);
        Task.ContinueWith(_ => cancellationTokenRegistration.Dispose());
    }
}