我想询问输入如何使用TPL Dataflow最佳地设计最佳架构。我还没有编写代码所以没有可以发布的示例代码。我不是在寻找代码(除非是自愿),但是对设计的帮助将非常感激:
要求如下:
我有3个核心数据块,它们以特定的方式相互依赖。 Datablock1是一个生成Foo1类型对象的生产者。 Datablock2应该订阅Foo1对象(来自Datablock1),并且可能(不是在每个Foo1上,受特定函数约束)产生Foo2对象,它存储在输出队列中以供其他数据块使用。 Datablock3还使用Foo1对象(来自Datablock1)并可能生成Datablock2使用的Foo3对象并转换为Foo2对象。
总之,这里是数据块以及它们每个产生和消费的内容:
另一个要求是在Datablock2和Datablock3中几乎同时处理相同的Foo1。如果Foo1对象首先被Datablock2使用,然后一旦Datablock2完成其工作,就会将相同的Foo1对象发布到Datablock3以使其完成工作。来自Datablock2的Foo2对象可以来自Foo1对象或Foo3对象上的操作。
我希望这是有道理的,如果目前还不清楚,我很乐意解释。
我的第一个想法是为3个数据块中的每一个创建TPL数据流块,并使它们处理不同对象类型的传入流。另一个想法是分割数据块并使每个数据块仅处理一个对象类型的流。您有什么建议,或者是否有更好的解决方案可行?
Svick已经帮助了Datablock1并且它已经运行了,我只是坚持如何将我当前的环境(如上所述)转换为TPL Dataflow。
非常感谢任何想法或指示。
答案 0 :(得分:4)
让我们将这个问题分成三个并单独解决。
第一个是如何有条件地生产物品。我认为最好的选择是使用TransformManyBlock
并让你的函数返回一个或零项的集合。
另一个选项是link the two blocks conditionally,以便忽略null
并在您不想生成任何内容时返回null
。
但是如果你这样做,你还必须将源链接到NullTarget
,以便null
不会保留在其输出缓冲区中。
第二个问题是如何将Foo1s发送到块#2和块#3。我可以在这里看到两种方式:
BroadcastBlock
。请注意这一点,因为BroadcastBlock
没有输出队列,所以如果目标块推迟了一个项目,则意味着它不会处理它。因此,在这种情况下,不应设置块#2和#3的BoundedCapacity
。如果你不这样做,它们将永远不会推迟,所有消息都将由两个块处理。Post()
(或更好,SendAsync()
)阻止#3。我不确定“几乎在同一时间”究竟是什么意思,但总的来说,TPL Dataflow不会对独立块的处理顺序做出任何保证。您可以使用a custom TaskScheduler
更改不同块的优先级,但我不确定这在此处是否有用。
最后也是最复杂的问题是如何在一个块中处理不同类型的项目。有几种方法可以做到这一点,虽然我不确定哪种方式最适合你:
TransformBlock<Foo1, Foo2>
和一个TransformBlock<Foo3, Foo2>
。然后,您可以将它们链接到一个BufferBlock<Foo2>
。BatchedJoinBlock<Foo1, Foo3>
,batchSize
为1.这意味着生成的Tuple<IList<Foo1>, IList<Foo3>>
将始终包含一个Foo1
或一个Foo3
。 BatchedJoinBlock
链接到产生更合适类型的TransformBlock
来增强以前的解决方案。这可以是Tuple<Foo1, Foo3>
(其中一个项目将始终为null
),或者类似于F#Choice<Foo1, Foo3>
,这可以确保只设置其中一个。ISourceBlock<Foo2>
并且还有两个属性:类型为Target1
的{{1}}和类型为ITarget<Foo1>
的{{1}},类似于内置的连接块。使用选项#1和#3,您还可以将块封装到一个自定义块中,该块看起来像是来自外部的#4块,因此它更容易重用。