我正试图在TPL Dataflow块中完成“完成”。特别是TransformBlock
似乎没有完成。为什么呢?
我的代码计算从1到1000的所有整数的平方。我使用了BufferBlock
和TransformBlock
。稍后在我的代码中,我等待完成TransformBlock
。该块实际上从未实际完成,我不明白为什么。
static void Main(string[] args)
{
var bufferBlock = new BufferBlock<int>();
var calculatorBlock = new TransformBlock<int, int>(i =>
{
Console.WriteLine("Calculating {0}²", i);
return (int)Math.Pow(i, 2);
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8 });
using (bufferBlock.LinkTo(calculatorBlock, new DataflowLinkOptions { PropagateCompletion = true }))
{
foreach (var number in Enumerable.Range(1, 1000))
{
bufferBlock.Post(number);
}
bufferBlock.Complete();
// This line never completes
calculatorBlock.Completion.Wait();
// Unreachable code
IList<int> results;
if (calculatorBlock.TryReceiveAll(out results))
{
foreach (var result in results)
{
Console.WriteLine("x² = {0}", result);
}
}
}
}
起初我以为我创造了一个死锁情况,但这似乎不是真的。当我在调试器中检查calculatorBlock.Completion
任务时,其Status
属性设置为WaitingForActivation
。那是我的大脑蓝色被筛选的那一刻。
答案 0 :(得分:8)
您的管道挂起的原因是,BufferBlock
和TransformBlock
显然都没有完成,直到他们清空了自己的项目(我猜想IPropagatorBlock
的所需行为虽然我还没有找到相关文件。)
这可以通过一个更小的例子进行验证:
var bufferBlock = new BufferBlock<int>();
bufferBlock.Post(0);
bufferBlock.Complete();
bufferBlock.Completion.Wait();
除非您在完成前添加bufferBlock.Receive();
,否则无限期阻止。
如果您在阻止之前通过TryReceiveAll
代码块阻止管道中的项目,将另一个ActionBlock
连接到管道,将TransformBlock
转换为ActionBlock
或任何其他方式,这将不再阻止。
关于您的具体解决方案,您似乎根本不需要BufferBlock
或TransformBlock
,因为块有自己的输入队列而您不会使用返回TransformBlock
的值。只需ActionBlock
:
var block = new ActionBlock<int>(
i =>
{
Console.WriteLine("Calculating {0}²", i);
Console.WriteLine("x² = {0}", (int)Math.Pow(i, 2));
},
new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 8});
foreach (var number in Enumerable.Range(1, 1000))
{
block.Post(number);
}
block.Complete();
block.Completion.Wait();
答案 1 :(得分:7)
我想我现在明白了。 TransformBlock
的实例不被视为&#34;完成&#34;直到满足以下条件:
TransformBlock.Complete()
被称为InputCount == 0
- 该块已将其转换应用于每个传入元素OutputCount == 0
- 所有已转换的元素都已离开输出缓冲区在我的程序中,没有链接到源TransformBlock
的目标块,因此源块永远不会刷新其输出缓冲区。
作为一种解决方法,我添加了第二个用于存储转换元素的BufferBlock
。
static void Main(string[] args)
{
var inputBufferBlock = new BufferBlock<int>();
var calculatorBlock = new TransformBlock<int, int>(i =>
{
Console.WriteLine("Calculating {0}²", i);
return (int)Math.Pow(i, 2);
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8 });
var outputBufferBlock = new BufferBlock<int>();
using (inputBufferBlock.LinkTo(calculatorBlock, new DataflowLinkOptions { PropagateCompletion = true }))
using (calculatorBlock.LinkTo(outputBufferBlock, new DataflowLinkOptions { PropagateCompletion = true }))
{
foreach (var number in Enumerable.Range(1, 1000))
{
inputBufferBlock.Post(number);
}
inputBufferBlock.Complete();
calculatorBlock.Completion.Wait();
IList<int> results;
if (outputBufferBlock.TryReceiveAll(out results))
{
foreach (var result in results)
{
Console.WriteLine("x² = {0}", result);
}
}
}
}
答案 2 :(得分:0)
TransformBlock需要一个ITargetBlock,他可以在其中发布转换。
var writeCustomerBlock = new ActionBlock<int>(c => Console.WriteLine(c));
transformBlock.LinkTo(
writeCustomerBlock, new DataflowLinkOptions
{
PropagateCompletion = true
});
此操作完成。