我有一个非常基本的线性管道,我想在其中传播完成并等到一切都完成:
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
完成,则addBlock
和mulBlock
的结果会被打印出来。如果我等待完成divBlock
,则addBlock
,mulBlock
和subBlock
的结果会被打印出来。
我的代码基于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
时终止。
答案 0 :(得分:2)
问题是只有在清空所有队列(包括输出队列)后块才会完成。在你的情况下发生的事情是完成传播正确,但然后divBlock
卡在&#34;几乎完成&#34;模式,等待其输出队列中的项目被删除。
要解决此问题,您可以将divBlock
更改为ActionBlock
,也可以将其与DataflowBlock.NullTarget<int>()
相关联。