ActionBlock に直接投稿するだけでなく (BoundedCapacity を使用して)、1 つまたは複数の ActionBlock にリンクされた BufferBlock を使用することに関連する利点があるかどうか疑問に思っていました (スロットリングが必要ない場合)。
3 に答える
あるブロックから他の複数のブロックにアイテムを転送するだけの場合は、は必要ありませんBufferBlock
。
しかし、それが役立つ場合も確かにあります。たとえば、複雑なデータフローネットワークがある場合は、それぞれが独自の方法で作成された、より小さなサブネットワークから構築することができます。これを行うには、ブロックのグループを表すための何らかの方法が必要です。BufferBlock
あなたが言及した場合、メソッドからそのシングル(おそらくとして)を返すことITargetBlock
は簡単な解決策でしょう。
便利なもう1つの例はBufferBlock
、複数のソースブロックから複数のターゲットブロックにアイテムを送信する場合です。仲介者として使用した場合はBufferBlock
、各ソースブロックを各ターゲットブロックに接続する必要はありません。
を使用できる例は他にもたくさんあると思いますBufferBlock
。もちろん、あなたのケースでそれを使用する理由が見当たらない場合は、使用しないでください。
svick の回答に追加すると、バッファブロックには別の利点があります。複数の出力リンクを持つブロックがあり、それらの間でバランスを取りたい場合は、出力ブロックを制限容量 1 に変更し、バッファブロックを追加してキューイングを処理する必要があります。
これは私たちがやろうとしていることです:
- 一部のコード ブロックは、Post(T t) メソッドを使用して BufferBlock にデータをポストします。
- この BufferBlock は、BufferBlock の LinkTo t) メソッドを使用して 3 つの ActionBlock インスタンスにリンクされています。
BufferBlock は、リンクされているすべてのターゲット ブロックに入力データのコピーを渡すわけではないことに注意してください。代わりに、1 つのターゲット ブロックのみに対して行われます。ここでは、1 つのターゲットがリクエストの処理でビジー状態になると、他のターゲットに引き渡されることを期待しています。では、以下のコードを参照してみましょう。
static void Main(string[] args)
{
BufferBlock<int> bb = new BufferBlock<int>();
ActionBlock<int> a1 = new ActionBlock<int>(a =>
{
Thread.Sleep(100);
Console.WriteLine("Action A1 executing with value {0}", a);
});
ActionBlock<int> a2 = new ActionBlock<int>(a =>
{
Thread.Sleep(50);
Console.WriteLine("Action A2 executing with value {0}", a);
});
ActionBlock<int> a3 = new ActionBlock<int>(a =>
{
Thread.Sleep(50);
Console.WriteLine("Action A3 executing with value {0}", a);
});
bb.LinkTo(a1);
bb.LinkTo(a2);
bb.LinkTo(a3);
Task t = new Task(() =>
{
int i = 0;
while (i < 10)
{
Thread.Sleep(50);
i++;
bb.Post(i);
}
}
);
t.Start();
Console.Read();
}
実行すると、次の出力が生成されます。
- 値 1 で実行中のアクション A1
- 値 2 で実行中のアクション A1
- 値 3 で実行するアクション A1
- 値 4 で実行されるアクション A1
- 値 5 で実行されるアクション A1
- 値 6 で実行されるアクション A1
- 値 7 で実行されるアクション A1
- 値 8 で実行されるアクション A1
- 値 9 で実行されるアクション A1
- 値 10 で実行するアクション A1
これは、(Thread.Sleep(100) が意図的に追加されたため) ビジーであっても、1 つのターゲットのみが実際にすべてのデータを実行していることを示しています。
これは、すべてのターゲット ブロックがデフォルトで貪欲であり、データを処理できない場合でも入力をバッファリングするためです。この動作を変更するために、以下に示すように、ActionBlock を初期化する際に、DataFlowBlockOptions で Bounded Capacity を 1 に設定しました。
static void Main(string[] args)
{
BufferBlock<int> bb = new BufferBlock<int>();
ActionBlock<int> a1 = new ActionBlock<int>(a =>
{
Thread.Sleep(100);
Console.WriteLine("Action A1 executing with value {0}", a);
}
, new ExecutionDataflowBlockOptions {BoundedCapacity = 1});
ActionBlock<int> a2 = new ActionBlock<int>(a =>
{
Thread.Sleep(50);
Console.WriteLine("Action A2 executing with value {0}", a);
}
, new ExecutionDataflowBlockOptions {BoundedCapacity = 1});
ActionBlock<int> a3 = new ActionBlock<int>(a =>
{
Thread.Sleep(50);
Console.WriteLine("Action A3 executing with value {0}", a);
}
, new ExecutionDataflowBlockOptions {BoundedCapacity = 1});
bb.LinkTo(a1);
bb.LinkTo(a2);
bb.LinkTo(a3);
Task t = new Task(() =>
{
int i = 0;
while (i < 10)
{
Thread.Sleep(50);
i++;
bb.Post(i);
}
});
t.Start();
Console.Read();
}
このプログラムの出力は次のとおりです。
- 値 1 で実行中のアクション A1
- 値 3 で実行するアクション A2
- 値 2 で実行中のアクション A1
- 値 6 で実行するアクション A3
- 値 7 で実行されるアクション A3
- 値 8 で実行されるアクション A3
- 値 5 で実行するアクション A2
- 値 9 で実行されるアクション A3
- 値 4 で実行されるアクション A1
- 値 10 で実行するアクション A2
これは明らかに、予想どおり 3 つの ActionBlock にまたがるデータの分布です。