[英]How can a TPL Dataflow block downstream get data produced by a source?
我正在使用TPL Dataflow處理圖像。 我收到處理請求,從流中讀取圖像,應用幾個轉換,然后將生成的圖像寫入另一個流:
Request -> Stream -> Image -> Image ... -> Stream
為此,我使用塊:
BufferBlock<Request>
TransformBlock<Request,Stream>
TransformBlock<Stream,Image>
TransformBlock<Image,Image>
TransformBlock<Image,Image>
...
writerBlock = new ActionBlock<Image>
問題是初始Request
是包含創建結果Stream
所需的一些數據以及此時我需要的一些其他信息。 我是否必須將原始Request
(或其他一些上下文對象) writerBlock
到所有其他塊的writerBlock
,如下所示:
TransformBlock<Request,Tuple<Request,Stream>>
TransformBlock<Tuple<Request,Stream>,Tuple<Request,Image>>
TransformBlock<Tuple<Request,Image>,Tuple<Request,Image>>
...
(這很難看),還是有辦法將第一個塊鏈接到最后一個塊(或者,推廣到需要附加數據的塊)?
是的,你幾乎需要做你所描述的,將每個塊中的附加數據傳遞給下一個塊。
但是使用幾個輔助方法,你可以使這更簡單:
public static IPropagatorBlock<TInput, Tuple<TOutput, TInput>>
CreateExtendedSource<TInput, TOutput>(Func<TInput, TOutput> transform)
{
return new TransformBlock<TInput, Tuple<TOutput, TInput>>(
input => Tuple.Create(transform(input), input));
}
public static IPropagatorBlock<Tuple<TInput, TExtension>, Tuple<TOutput, TExtension>>
CreateExtendedTransform<TInput, TOutput, TExtension>(Func<TInput, TOutput> transform)
{
return new TransformBlock<Tuple<TInput, TExtension>, Tuple<TOutput, TExtension>>(
tuple => Tuple.Create(transform(tuple.Item1), tuple.Item2));
}
簽名看起來令人生畏,但實際上並沒有那么糟糕。
此外,您可能希望添加將選項傳遞給創建的塊的重載,或者重載以獲取異步委托的重載。
例如,如果您想使用單獨的塊對數字執行某些操作,同時沿途傳遞原始數字,您可以執行以下操作:
var source = new BufferBlock<int>();
var divided = CreateExtendedSource<int, double>(i => i / 2.0);
var formatted = CreateExtendedTransform<double, string, int>(d => d.ToString("0.0"));
var writer = new ActionBlock<Tuple<string, int>>(tuple => Console.WriteLine(tuple));
source.LinkTo(divided);
divided.LinkTo(formatted);
formatted.LinkTo(writer);
for (int i = 0; i < 10; i++)
source.Post(i);
如您所見,您的lambda(除了最后一個)只處理“當前”值( int
, double
或string
,具體取決於管道的階段),“原始”值(總是int
)自動傳遞。 在任何時候,您都可以使用使用普通構造函數創建的塊來訪問這兩個值(例如示例中的最終ActionBlock
)。
(那個BufferBlock
實際上並不是必需的,但我添加它以更BufferBlock
您的設計。)
因為我剛剛開始使用TPL Dataflow,所以我可能會過頭了。 但我相信你可以使用BroadcastBlock
作為源和你的第一個目標之間的媒介來實現這一點。
BroadcastBlock
可以提供消息給許多目標,所以你用它來提供給您的目標, 同時也為JoinBlock
,在將結果與原始郵件合並結束。
source -> Broadcast ->-----------------------------------------> JoinBlock <source, result>
-> Transformation1 -> Transformation 'n' ->
例如:
var source = new BufferBlock<int>();
var transformation = new TransformBlock<int, int>(i => i * 100);
var broadCast = new BroadcastBlock<int>(null);
source.LinkTo(broadCast);
broadCast.LinkTo(transformation);
var jb = new JoinBlock<int, int>();
broadCast.LinkTo(jb.Target1);
transformation.LinkTo(jb.Target2);
jb.LinkTo(new ActionBlock<Tuple<int, int>>(
c => Console.WriteLine("Source:{0}, Target Result: {1}", c.Item1, c.Item2)));
source.Post(1);
source.Post(2);
source.Complete();
收益率...
資料來源:1,目標結果:100
資料來源:2,目標結果:200
我不太確定它在異步環境中的表現。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.