gpt4 book ai didi

c# - 最佳 TPL 数据流设计?

转载 作者:太空宇宙 更新时间:2023-11-03 21:58:25 24 4
gpt4 key购买 nike

我想征求意见,了解如何使用 TPL 数据流设计最佳架构。我还没有编写代码,所以没有我可以发布的示例代码。我也不是在寻找代码(除非自愿),但非常感谢设计方面的帮助:

要求如下:

我有 3 个核心数据 block ,它们以特定方式相互依赖。 Datablock1 是生产 Foo1 类型对象的生产者。 Datablock2 应该订阅 Foo1 对象(来自 Datablock1)并且可能(不是在每个 Foo1 上,受特定函数约束)生成 Foo2 对象,它存储在输出队列中供其他数据 block 使用。 Datablock3 还使用 Foo1 对象(来自 Datablock1)并可能生成 Foo3 对象,Datablock2 使用这些对象并将其转换为 Foo2 对象。

总而言之,这里是数据 block 以及它们各自生产和消费的内容:

  • Datablock1:生产(Foo1),消费(无)
  • Datablock2:生产(Foo2),消费(Foo1,Foo3)
  • Datablock3:生产(Foo3),消费(Foo1)

一个额外的要求是相同的 Foo1 在 Datablock2 和 Datablock3 中几乎同时被处理。如果 Foo1 对象首先被 Datablock2 使用,然后一旦 Datablock2 完成其工作,将完全相同的 Foo1 对象发布到 Datablock3 以使其完成工作,那就没问题了。来自 Datablock2 的 Foo2 对象可以来自对 Foo1 对象或 Foo3 对象的操作。

我希望这是有道理的,如果仍然不清楚,我很乐意解释更多。

我的第一个想法是为 3 个数据 block 中的每一个创建 TPL 数据流 block ,并让它们处理不同对象类型的传入流。另一个想法是拆分数据 block ,让每个数据 block 只处理一种单一对象类型的流。您有什么建议或是否有更好的解决方案?

Svick 已经在 Datablock1 上提供了帮助并且它已经可以运行了,我只是想知道如何将我当前的环境(如上所述)转换为 TPL 数据流。

非常感谢任何想法或指示。

最佳答案

让我们将这个问题分成三个部分并分别解决。

第一个是如何有条件地生产元素。我认为最好的选择是使用 TransformManyBlock 并让您的函数返回一个包含一个或零个项目的集合。

另一种选择是 link the two blocks conditionally ,所以 null s 被忽略并返回 null当你不想生产任何东西时。但如果你这样做,你还必须将源链接到 NullTarget ,所以 null s 不会留在其输出缓冲区中。

第二个问题是如何将 Foo1s 发送到 block #2 和 block #3。我可以在这里看到两种方式:

  1. 使用BroadcastBlock链接到两个目标 block (#2 和#3)。小心这个,因为BroadcastBlock没有输出队列,所以如果一个目标 block 推迟了一个项目,就意味着它不会处理它。因此,您不应设置 BoundedCapacity在这种情况下, block #2 和#3。如果您不这样做,它们将永远不会推迟,并且所有消息都将由两个 block 处理。
  2. 在 block #2 处理 Foo1 之后,手动 Post() (或更好,SendAsync())阻止#3。

我不确定“大约同时”到底是什么意思,但总的来说,TPL Dataflow 不对独立 block 的处理顺序做出任何保证。您可以使用 a custom TaskScheduler 更改不同 block 的优先级,但我不确定这在这里是否有用。

最后也是最复杂的问题是如何在单个 block 中处理不同类型的项目。有几种方法可以做到这一点,但我不确定哪种方法最适合您:

  1. 不要在一个 block 中处理它们。有一个TransformBlock<Foo1, Foo2>和一个TransformBlock<Foo3, Foo2> .然后您可以将它们都链接到一个 BufferBlock<Foo2> .
  2. 按照您的建议,使用 BatchedJoinBlock<Foo1, Foo3> , 与 batchSize的 1。这意味着结果 Tuple<IList<Foo1>, IList<Foo3>>将始终包含其中一个 Foo1或一个 Foo3 .
  3. 通过链接 BatchedJoinBlock 来增强以前的解决方案到 TransformBlock产生更合适的类型。那可能是 Tuple<Foo1, Foo3> (其中一项将始终为 null ),或类似于 F# Choice<Foo1, Foo3> 的内容,这确保只设置两者之一。
  4. 从头开始创建一个新的 block 类型,它完全符合您的要求。应该是ISourceBlock<Foo2>并且还有两个属性:Target1类型 ITarget<Foo1>Target2类型 ITarget<Foo3> ,就像内置的连接 block 。

使用选项 #1 和 #3,您还可以将 block 封装到单个自定义 block 中,从外部看起来像 #4 中的 block ,因此更容易重用。

关于c# - 最佳 TPL 数据流设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11242144/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com