C# 多对多TPL数据流不会处理所有输入

C# 多对多TPL数据流不会处理所有输入,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,我有一个TPL Datalow管道,其中有两个源和两个目标以多对多的方式链接。目标块似乎成功完成,但通常会丢弃一个或多个输入。我在下面附上了我能想到的最简单的完整复制。有什么想法吗 注: 只有在生成输入时使用人工延迟时,才会出现问题 已成功为两个源调用Complete(),但其中一个源的完成任务挂起在WaitingForActivation状态,即使两个目标都已成功完成 我找不到任何说明不支持多对多数据流的文档,这个问题的答案暗示它是- 使用系统; 使用系统诊断; 使用System.Linq;

我有一个TPL Datalow管道,其中有两个源和两个目标以多对多的方式链接。目标块似乎成功完成,但通常会丢弃一个或多个输入。我在下面附上了我能想到的最简单的完整复制。有什么想法吗

注:

  • 只有在生成输入时使用人工延迟时,才会出现问题
  • 已成功为两个源调用Complete(),但其中一个源的完成任务挂起在WaitingForActivation状态,即使两个目标都已成功完成
  • 我找不到任何说明不支持多对多数据流的文档,这个问题的答案暗示它是-
  • 使用系统;
    使用系统诊断;
    使用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 });