Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# TPL数据流,JoinBlock限制的替代方案?_C#_Concurrency_Task Parallel Library_Tpl Dataflow_Actor Model - Fatal编程技术网

C# TPL数据流,JoinBlock限制的替代方案?

C# TPL数据流,JoinBlock限制的替代方案?,c#,concurrency,task-parallel-library,tpl-dataflow,actor-model,C#,Concurrency,Task Parallel Library,Tpl Dataflow,Actor Model,我寻找JoinBlock的替代品,它可以通过n-TransformBlocks链接,并将所有TransformBlock源块的消息连接/合并在一起,以便将此类消息的集合传递给另一个数据流块 JoinBlock可以很好地完成这项工作,但它仅限于连接3个源块。它还存在相当多的低效率问题(连接两个源块的偶数值类型(Int)的速度非常慢)。有没有办法让任务从TransformBlocks返回,并等到所有TransformBlocks都有一个完成的任务要传递,然后再接受任务 有其他的想法吗?我可能有1-2

我寻找JoinBlock的替代品,它可以通过n-TransformBlocks链接,并将所有TransformBlock源块的消息连接/合并在一起,以便将此类消息的集合传递给另一个数据流块

JoinBlock可以很好地完成这项工作,但它仅限于连接3个源块。它还存在相当多的低效率问题(连接两个源块的偶数值类型(Int)的速度非常慢)。有没有办法让任务从TransformBlocks返回,并等到所有TransformBlocks都有一个完成的任务要传递,然后再接受
任务

有其他的想法吗?我可能有1-20个这样的转换块,在传递连接的项集合之前,我需要将这些项连接在一起。每个转换块保证为每个输入项“转换”返回一个输出项

编辑:要求澄清:

根据我之前的一个问题,我将JoinBlocks设置如下:

public Test()
{
    broadCastBlock = new BroadcastBlock<int>(i =>
        {
            return i;
        });

    transformBlock1 = new TransformBlock<int, int>(i =>
        {
            return i;
        });

    transformBlock2 = new TransformBlock<int, int>(i =>
        {
            return i;
        });

    joinBlock = new JoinBlock<int, int>();

    processorBlock = new ActionBlock<Tuple<int, int>>(tuple =>
        {
            //Console.WriteLine("tfb1: " + tuple.Item1 + "tfb2: " + tuple.Item2);
        });

    //Linking
    broadCastBlock.LinkTo(transformBlock1, new DataflowLinkOptions { PropagateCompletion = true });
    broadCastBlock.LinkTo(transformBlock2, new DataflowLinkOptions { PropagateCompletion = true });
    transformBlock1.LinkTo(joinBlock.Target1);
    transformBlock2.LinkTo(joinBlock.Target2);
    joinBlock.LinkTo(processorBlock, new DataflowLinkOptions { PropagateCompletion = true });
}

public void Start()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();

    const int numElements = 1000000;

    for (int i = 1; i <= numElements; i++)
    {
        broadCastBlock.Post(i);
    }

    ////mark completion
    broadCastBlock.Complete();
    Task.WhenAll(transformBlock1.Completion, transformBlock2.Completion).ContinueWith(_ => joinBlock.Complete());


    processorBlock.Completion.Wait();

    watch.Stop();

    Console.WriteLine("Time it took: " + watch.ElapsedMilliseconds + " - items processed per second: " + numElements / watch.ElapsedMilliseconds * 1000);
    Console.ReadLine();
}
公共测试()
{
broadCastBlock=新的broadCastBlock(i=>
{
返回i;
});
transformBlock1=新的TransformBlock(i=>
{
返回i;
});
transformBlock2=新的TransformBlock(i=>
{
返回i;
});
joinBlock=新的joinBlock();
processorBlock=新动作块(元组=>
{
//Console.WriteLine(“tfb1:+tuple.Item1+”tfb2:+tuple.Item2);
});
//连接
broadCastBlock.LinkTo(transformBlock1,新的DataflowLinkOptions{PropagateCompletion=true});
broadCastBlock.LinkTo(transformBlock2,新的DataflowLinkOptions{PropagateCompletion=true});
transformBlock1.LinkTo(joinBlock.Target1);
transformBlock2.LinkTo(joinBlock.Target2);
LinkTo(processorBlock,新数据流链接选项{PropagateCompletion=true});
}
公开作废开始()
{
秒表=新秒表();
watch.Start();
常数整数=1000000;
for(int i=1;i joinBlock.Complete());
processorBlock.Completion.Wait();
看,停;
Console.WriteLine(“花费的时间:“+watch.elapsedmillesons+”-每秒处理的项目:“+numElements/watch.elapsedmillesons*1000”);
Console.ReadLine();
}

一种方法是使用
BatchBlock
并将
Greedy
设置为
false
。在此配置中,块不会执行任何操作,直到来自
n
不同块的
n
项等待使用(其中
n
是创建
BatchBlock
时设置的数字)。当这种情况发生时,它一次消耗所有
n
项,并生成一个包含所有项的数组


此解决方案的一个警告是,生成的数组没有排序:您不知道哪个项来自哪个源。我不知道它的性能与
JoinBlock
相比如何,您必须自己进行测试。(虽然我可以理解,如果以这种方式使用
BatchBlock
会比较慢,因为非贪婪消费需要开销。)

如果您想对每个项目执行多个并行操作,那么在单个块内执行这些操作更为合理,而不是将它们拆分为多个块,然后再次尝试将独立结果合并到单个对象中。所以我的建议是这样做:

var block = new TransformBlock<MyClass, MyClass>(async item =>
{
    Task<SomeType1> task1 = Task.Run(() => CalculateProperty1(item.Id));
    Task<SomeType2> task2 = Task.Run(() => CalculateProperty2(item.Id));
    await Task.WhenAll(task1, task2).ConfigureAwait(false);
    item.Property1 = task1.Result;
    item.Property2 = task2.Result;
    return item;
}, new ExecutionDataflowBlockOptions()
{
    MaxDegreeOfParallelism = 2
});
var block=newtransformblock(异步项=>
{
Task task1=Task.Run(()=>CalculateProperty1(item.Id));
tasktask2=Task.Run(()=>CalculateProperty2(item.Id));
wait Task.WhenAll(task1,task2).configurewait(false);
item.Property1=task1.结果;
item.Property2=任务2.结果;
退货项目;
},新的ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism=2
});
在上面的示例中,
MyClass
类型的项通过
TransformBlock
传递。每个项目的属性
Property1
Property2
使用每个属性的单独
任务
并行计算。然后等待这两个任务,当两个任务都完成时,结果将分配给项目的属性。最后返回已处理的项目


使用这种方法唯一需要注意的是,并行度将是内部并行操作和块选项的乘积。因此,在上面的例子中,平行度为2x2=4。准确地说,这将是最大并行度,因为两个内部计算中的一个可能比另一个慢。因此,在任何给定的时刻,实际的并行度都可能在2到4之间。

“有没有一种方法可以让任务从TransformBlocks返回,并等到所有TransformBlocks都有一个完成的任务可以传递,然后再接受
任务”
“我不知道为什么你认为这会有帮助,但TDF块不是这样工作的。您要么接受一个项目,要么不接受,您不能接受一个项目并在以后决定接受它。我将测试它并报告与连接块相关的性能数字。然而,我也对其他非TDF解决方案感兴趣。一些关于BatchBlock的反馈:你是对的,非贪婪模式下的BatchBlock(需要获取所有项目,而不仅仅是第一个可用项目)会显著降低速度。我决定在TDF之外解决这个问题。我仍然希望在前面的每个变换块中并行运行func,然后按常规连接结果,而不是通过TDF。但是谢谢你给batchBlock的指针。@svick-很好地重新调整了batchBlock的用途,以补偿JoinBlock有限的输出元组数量。