C# 多对多TPL数据流不会处理所有输入
我有一个TPL Datalow管道,其中有两个源和两个目标以多对多的方式链接。目标块似乎成功完成,但通常会丢弃一个或多个输入。我在下面附上了我能想到的最简单的完整复制。有什么想法吗 注:C# 多对多TPL数据流不会处理所有输入,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,我有一个TPL Datalow管道,其中有两个源和两个目标以多对多的方式链接。目标块似乎成功完成,但通常会丢弃一个或多个输入。我在下面附上了我能想到的最简单的完整复制。有什么想法吗 注: 只有在生成输入时使用人工延迟时,才会出现问题 已成功为两个源调用Complete(),但其中一个源的完成任务挂起在WaitingForActivation状态,即使两个目标都已成功完成 我找不到任何说明不支持多对多数据流的文档,这个问题的答案暗示它是- 使用系统; 使用系统诊断; 使用System.Linq;
使用系统;
使用系统诊断;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
使用System.Threading.Tasks.Dataflow;
班级计划
{
私有常量int NumbersPerSource=10;
private const int maxDelay毫秒=10;
静态异步任务主(字符串[]args)
{
int numbersProcessed=0;
var source1=新的缓冲块();
var source2=新的缓冲块();
var target1=新动作块(i=>Interlocked.Increment(ref numbersProcessed));
var target2=新动作块(i=>Interlocked.Increment(ref numbersProcessed));
var linkOptions=new DataflowLinkOptions(){PropagateCompletion=true};
source1.LinkTo(target1,linkOptions);
资料来源1.链接到(target2,链接选项);
资料来源2.链接到(target1,链接选项);
source2.LinkTo(target2,linkOptions);
var task1=Task.Run(()=>Post(source1));
var task2=Task.Run(()=>Post(source2));
//source1或source2完成任务可能永远不会完成,即使始终成功调用complete。
//等待Task.WhenAll(task1、task2、source1.Completion、source2.Completion、target1.Completion、target2.Completion);
等待任务完成(任务1,任务2,目标1.完成,目标2.完成);
WriteLine($“{NumbersPerSource*2}个已处理数字中的{numbersProcessed}”);
}
专用静态异步任务Post(缓冲块源)
{
foreach(可枚举范围(0,NumberPersource)中的变量i){
wait Task.Delay(TimeSpan.frommilluses(getrandommilluses());
Assert(source.Post(i));
}
source.Complete();
}
私有静态随机=新随机();
私有静态int-getRandom毫秒()
{
锁(随机){
返回Random.Next(0,最大延迟毫秒);
}
}
}
正如@MikeJ在a中指出的,在多对多数据流配置中使用传播完成
链接块可能会导致一些目标块过早完成。在这种情况下,当两个源块中的任何一个完成时,target1
和target2
都标记为已完成,使另一个源无法完成,因为其输出缓冲区中仍有消息。这些消息永远不会被使用,因为链接的目标块都不愿意接受它们
要解决此问题,您可以使用下面的自定义方法:
public static async void PropagateCompletion(IDataflowBlock[] sources,
IDataflowBlock[] targets)
{
// Arguments validation omitted
Task allSourcesCompletion = Task.WhenAll(sources.Select(s => s.Completion));
try { await allSourcesCompletion.ConfigureAwait(false); } catch { }
var exception = allSourcesCompletion.IsFaulted ?
allSourcesCompletion.Exception : null;
foreach (var target in targets)
{
if (exception != null) target.Fault(exception); else target.Complete();
}
}
用法示例:
source1.LinkTo(target1);
source1.LinkTo(target2);
source2.LinkTo(target1);
source2.LinkTo(target2);
PropagateCompletion(new[] { source1, source2 }, new[] { target1, target2 });
请注意,在本例中,将源链接到目标时,不会传递任何DataflowLinkOptions
。问题在于,由于您已链接到两个源,并且正在使用PropagateCompletion,第一个要完成的源将关闭两个目标。因此,第二个来源最终发布到一个封闭的目标区块,而你错过了一些数字。啊,听起来可能是这样。唯一让我抓狂的是对source.Post的调用返回true。我认为如果没有目标接受它,它将返回false。我还得再深入一点,看看预期的行为是什么。谢谢相关:,还有。更大的问题可能是,你想通过发布这样的多对多来解决什么问题?我有一个financial ticker数据流,我将其拆分为多个数据流,每个数据流处理不同的指标(移动平均数等)。然后,这些数据被整理回一个数据流,所有数据点都被发送到交易策略以生成交易信号。还有其他数据源,如新闻提要和数据库数据,它们也被整理/合并到最终输出中,但股票代码数据被多次计算使用,因此这是多对多数据流的主要原因。
source1.LinkTo(target1);
source1.LinkTo(target2);
source2.LinkTo(target1);
source2.LinkTo(target2);
PropagateCompletion(new[] { source1, source2 }, new[] { target1, target2 });