ITargetBlock的最佳实践<TInput> .Completion.ContinueWith()

Mat*_*son 5 .net c# task-parallel-library async-await tpl-dataflow

这个问题是关于ContinueWith()用于处理TPL数据块完成时的最佳实践.

ITargetBlock<TInput>.Completion()方法允许您使用异步处理数据块的完成ContinueWith().

请考虑以下Console应用程序代码,它演示了一个非常基本的用法

private static void Main()
{
    test().Wait();
}

static async Task test()
{
    var transform = new TransformBlock<int, double>(i => i/2.0);
    var output    = new ActionBlock<double>(d => Console.WriteLine(d));

    // Warning CS4014 here:
    transform.Completion.ContinueWith(continuation => output.Complete());
    transform.LinkTo(output);

    for (int i = 0; i < 10; ++i)
        await transform.SendAsync(i);

    transform.Complete();
    await output.Completion;
}
Run Code Online (Sandbox Code Playgroud)

代码有一个非常简单的方法TransformBlock,它将整数除以2.0并将它们转换为双精度数.转换的数据由ActionBlock只处理控制台窗口的值处理.

输出是:

0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
Run Code Online (Sandbox Code Playgroud)

TransformBlock完成后,我也想完成ActionBlock.这样做很方便:

transform.Completion.ContinueWith(continuation => output.Complete());
Run Code Online (Sandbox Code Playgroud)

这就是问题所在.因为这是一个async方法,我忽略了返回值ContinueWith(),我得到一个编译器警告:

警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.

很明显,我不能await按照警告的建议进行呼叫 - 如果我这样做,代码会挂断,因为CompleteWith()任务transform.Complete()在调用之前无法完成.

现在我处理警告本身没有问题(我可以压缩它或将任务分配给变量,或使用.Forget()任务扩展等) - 但这是我的问题:

  • 以这种方式处理链接的DataBlock是否安全?
  • 有没有更好的办法?
  • 这里有什么重要我忽略了吗?

我认为如果我output.Complete()在继续之外做任何其他事情,我必须提供异常处理 - 但也许只是召唤output.Complete()充满危险?

i3a*_*non 7

虽然您可以自己实现完成传播,但TPL Dataflow可以为您完成DataflowLinkOptions.PropagateCompletion:

transform.LinkTo(output, new DataflowLinkOptions {PropagateCompletion = true});
Run Code Online (Sandbox Code Playgroud)

如果您仍希望自己实现,则可以解决存储任务的问题,并在适当时使用Task.WhenAll以下方法等待:

static async Task test()
{
    var transform = new TransformBlock<int, double>(i => i/2.0);
    var output    = new ActionBlock<double>(d => Console.WriteLine(d));

    // Warning CS4014 here:
    var continuation = transform.Completion.ContinueWith(_ => output.Complete());
    transform.LinkTo(output);

    for (int i = 0; i < 10; ++i)
        await transform.SendAsync(i);

    transform.Complete();
    await Task.WhenAll(continuation, output.Completion)
}
Run Code Online (Sandbox Code Playgroud)

这使您可以处理output.Complete和删除警告中的任何异常.

  • 真棒!这是答案,谢谢.(我对数据块如何处理异常的调查引发了一些我可能会在未来的某些时候提出的问题...;) (2认同)