如何在第一个异常中取消整个tpl dataFlow?

时间:2017-03-09 15:04:35

标签: c# task-parallel-library dataflow

考虑以下示例:

        ActionBlock<TimeSpan> ab = new ActionBlock<TimeSpan>(async _ =>
        {
            await Task.Delay(_);
            throw new Exception();
        }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Int32.MaxValue });

        ab.Post(TimeSpan.FromSeconds(10d));
        ab.Post(TimeSpan.FromDays(1d));

        await ab.Completion;

正如预期的那样,我在输出窗口中看到在10秒后抛出异常,但这不会导致dataFlow完成(await ab.Completion之后的断点在1天之前不会被命中)。

就我而言,如果在一个计算步骤中出现异常,我想取消整个dataFlow。

我无法看到如何使用tpl数据流... 有什么建议吗?

...谢谢

[编辑]正如Ofir所提到的,我可以这样做:

        ActionBlock<TimeSpan> ab = new ActionBlock<TimeSpan>(async _ =>
        {
            try
            {
                await Task.Delay(_);
                throw new Exception();// Or any other Task thay may throw an exception.
            }
            catch(Exception)
            {
                cancelTokenSource.Cancel();
                throw;
            }
        }, new ExecutionDataflowBlockOptions() {CancellationToken=cancelTokenSource.Token, MaxDegreeOfParallelism = Int32.MaxValue });

        ab.Post(TimeSpan.FromSeconds(10d));
        ab.Post(TimeSpan.FromDays(1d));

        await ab.Completion;

但这正是我想要避免的:输入相同的try {}一次又一次地抓住......或者更糟:忘掉它......;)

接下来我要做的就是为ActionBlock的构造函数编写一个替换来处理它(它需要额外的CancellationTokenSource参数)... 我很惊讶这不直接来自数据流......真的是这样吗?

[最终编辑] 似乎答案是在tpd dataFlow 中没有这样的东西,并且ActionBlock "extension constructor" (in fact a static method)接受CancellationTokenSource作为参数将是一种可能的解决方法......

1 个答案:

答案 0 :(得分:4)

您投掷的Exception会将ActionBlock置于故障状态 并将删除所有缓冲的邮件,并且不会接受更多邮件 对于CancelationToken(可以在ExecutionDataflowBlockOptions

中提供,也是如此

完成当前处理的消息后,您将在AggregateException上收到await ab.Completion

Task一样,您必须自己处理已执行邮件的堕胎。

为了演示,在您提供的示例中,可以像这样实现:

var cancellationTokenSource = new CancellationTokenSource();
var ab = new ActionBlock<TimeSpan>(async _  =>
{
    // await with cancellation token
    await Task.Delay(_, cancellationTokenSource.Token);
    cancellationTokenSource.Cancel();
}, new ExecutionDataflowBlockOptions {CancellationToken = cancellationTokenSource.Token, MaxDegreeOfParallelism = int.MaxValue});

ab.Post(TimeSpan.FromSeconds(10));
ab.Post(TimeSpan.FromSeconds(20));
Thread.Sleep(15000);
cancellationTokenSource.Cancel();
ab.Post(TimeSpan.FromSeconds(100));

try { await ab.Completion; }
catch(TaskCancelationException ex)
{ }

在上面的场景中,我们发布了2条将立即运行的消息 10秒后,第一条消息将导致Cancel cancellationTokenSource并导致另一条消息(延迟20秒)立即完成,并将ActionBlock置于取消状态。 我们尝试发布的下一条消息不被接受,也不会被执行。

15秒后,我们会在等待TaskCancelationException时获得completion