传播完成

时间:2016-08-07 05:13:46

标签: c# task-parallel-library tpl-dataflow

我有一个非常基本的线性管道,我想在其中传播完成并等到一切都完成:

static void Main(string[] args)
{
    ExecutePipeline().Wait();
}

static async Task ExecutePipeline()
{
    var addBlock = new TransformBlock<int, int>(x =>
    {
        var result = x + 2;
        Console.WriteLine(result);
        return result;
    });
    var subBlock = new TransformBlock<int, int>(x =>
    {
        var result = x - 2;
        Console.WriteLine(result);
        return result;
    });
    var mulBlock = new TransformBlock<int, int>(x =>
    {
        var result = x * 2;
        Console.WriteLine(result);
        return result;
    });
    var divBlock = new TransformBlock<int, int>(x =>
    {
        var result = x / 2;
        Console.WriteLine(result);
        return result;
    });

    var flowOptions = new DataflowLinkOptions { PropagateCompletion = true };
    addBlock.LinkTo(mulBlock, flowOptions);
    mulBlock.LinkTo(subBlock, flowOptions);
    subBlock.LinkTo(divBlock, flowOptions);

    addBlock.Post(4);
    addBlock.Complete();
    mulBlock.Complete();
    subBlock.Complete();
    await divBlock.Completion;
}

不幸的是,在当前状态下,只有addBlock的结果被打印并且程序终止,而不是在终止之前打印所有结果。

如果我注释掉所有在其块上调用Complete()的行,或者如果我将addBlock.Complete()取消注释,我会在管道中打印出所有结果,但程序永远不会结束,因为完成没有传播。但是,如果我取消阻止mulBlock.Complete()subBlock.Complete(),与默认代码的行为类似,程序会打印出addBlock的结果并终止。

有趣的是,取消注释最后提到的两个块中的任何一个或者它们都具有相同的行为,这使我质疑如果其中一个被注释,完成传播的方式。显然,我在逻辑中缺少一些东西,但我无法弄清楚它是什么。如何实现打印所有结果所需的行为?

修改

所以,我终于在https://stackoverflow.com/a/26803579/2006048

找到了适合我的东西

看来我需要将最后一段代码更改为:

addBlock.Post(4);
addBlock.Complete();
await addBlock.Completion;

原始代码不起作用,因为在数据传播之前在每个块上调用了Complete(),所以这是竞争条件的情况。

但是,使用这个新编辑的代码,它会在Complete()上调用addBlock并等待其完成。这使程序按预期工作,但让我更加困惑。为什么Completion必须等待addBlock而不是divBlock中的最后一个块?我认为Completion()仅在addBlock上调用,因为PropagationCompletion设置为true,但我认为我们会等待最后一个块的完成,而不是第一个。

如果我等待mulBlock的完成,则只会打印addBlock的结果。如果我等待subBlock完成,则addBlockmulBlock的结果会被打印出来。如果我等待完成divBlock,则addBlockmulBlocksubBlock的结果会被打印出来。

我的代码基于Stephen Cleary的 Concurrency in C#Cookbook 示例(第4.1节链接块(页52)):

var multiplyBlock = new TransformBlock<int, int>(item => item * 2);
var subtractBlock = new TransformBlock<int, int>(item => item - 2);

var options = new DataflowLinkOptions { PropagateCompletion = true };
multiplyBlock.LinkTo(subtractBlock, options);

...

// The first block's completion is automatically propagated to the second block.
multiplyBlock.Complete();
await subtractBlock.Completion;

当我设置Cleary的代码以匹配我所拥有的代码时,表现出相同的行为。程序打印结果并仅在我等待multiplyBlock.Completion时终止。

1 个答案:

答案 0 :(得分:2)

问题是只有在清空所有队列(包括输出队列)后块才会完成。在你的情况下发生的事情是完成传播正确,但然后divBlock卡在&#34;几乎完成&#34;模式,等待其输出队列中的项目被删除。

要解决此问题,您可以将divBlock更改为ActionBlock,也可以将其与DataflowBlock.NullTarget<int>()相关联。