2012-10-08 20 views

答えて

15

1ブロックから複数のアイテムにアイテムを転送するだけの場合は、BufferBlockは必要ありません。

しかし、確かに有用な場合があります。たとえば、複雑なデータフローネットワークを使用している場合、独自の方法で作成されたより小さなサブネットワークから構築することができます。これを行うには、ブロックのグループを表現するための方法が必要です。あなたが言及したケースでは、その単一のBufferBlock(おそらくITargetBlock)を返すのは簡単な解決策になります。

BufferBlockが便利なもう1つの例は、複数のソースブロックから複数のターゲットブロックにアイテムを送信する場合です。 BufferBlockを仲介者として使用した場合、各ソースブロックを各ターゲットブロックに接続する必要はありません。

BufferBlockを使用できる他の多くの例があります。もちろん、あなたのケースでそれを使用する理由がない場合は、しないでください。

+0

私はBufferBlocksを使用して、データフローブロック間の通信の「クリーン」な方法ですが、それだけの価値BufferBlocksを使用してのオーバーヘッド(もしあれば)であることを感じていますか? – Dimitri

+1

これはあなたが決定するためのものです。コードがきれいになっていると感じたら、それをしてください。いくつかのオーバーヘッドがありますが、パフォーマンスを本当に気にしない限り、気にならないはずです。 – svick

19

svickの答えに追加するには、バッファブロックの別の利点があります。複数の出力リンクを持つブロックがあり、それらの間のバランスを取る必要がある場合は、出力ブロックを貪欲でないものにし、キューブロックを処理するバッファブロックを追加する必要があります。

  • いくつかのコードブロックは、それはだ使用BufferBlockにデータを掲載します:

    これは、我々が行うことを計画しているものです。今死んでいるリンクより引用

    :私は便利な次の例を見つけましたポスト(T t)法。

  • このBufferBlockは、BufferBlockのLinkTo t)メソッドを使用して3つのActionBlockインスタンスにリンクされています。

BufferBlockは、入力データのコピーをリンク先のすべてのターゲットブロックに渡すことはありません。その代わりに、1つのターゲットブロックのみに処理されます。ここでは、1つのターゲットが他のターゲットに引き渡されます。

  • アクションA1が1
  • アクションA1が値2
  • アクションA1が実行して実行値で実行:それは次の出力を生成し、実行すると

    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(); 
        } 
    

    :それでは、以下のコードを参照してみましょう値3を持つ

  • 値1で実行するアクションA1
  • 値5で実行するアクションA1
  • 値10
と実行値9
  • アクションA1と実行値8
  • アクションA1と実行値7
  • アクションA1と実行値6
  • アクションA1で実行するアクションA1

    これは、(Thread.Sleep(100)が意図的に追加されたため)ビジー状態であっても実際にすべてのデータを実行しているターゲットが1つだけであることを示しています。

    これは、すべてのターゲットブロックがデフォルトで貪欲であり、データを処理できない場合でも入力をバッファリングするためです。この動作を変更するには、DataFlowBlockOptionsでGreedyプロパティをfalseに設定しました。次のようにActionBlockを実行します。

    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 DataflowBlockOptions(taskScheduler: TaskScheduler.Default, 
                          maxDegreeOfParallelism: 1, maxMessagesPerTask: 1, 
                          cancellationToken: CancellationToken.None, 
                          //Not Greedy 
                          greedy: false) 
                   ); 
    
         ActionBlock<int> a2 = new ActionBlock<int>((a) => 
                    { 
                     Thread.Sleep(50); 
                     Console.WriteLine("Action A2 executing with value {0}", a); 
                    } 
                    , new DataflowBlockOptions(taskScheduler: TaskScheduler.Default, 
                          maxDegreeOfParallelism: 1, maxMessagesPerTask: -1, 
                          cancellationToken: CancellationToken.None, 
                          greedy: false) 
                   ); 
         ActionBlock<int> a3 = new ActionBlock<int>((a) => 
                    { 
                     Thread.Sleep(50); 
                     Console.WriteLine("Action A3 executing with value {0}", a); 
                    } 
                    , new DataflowBlockOptions(taskScheduler: TaskScheduler.Default, 
                          maxDegreeOfParallelism: 1, maxMessagesPerTask: -1, 
                          cancellationToken: CancellationToken.None, 
                          greedy: false) 
                   ); 
         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(); 
        } 
    

    このプログラムの出力は次のとおり

    • アクションA1が値1
    • 値2
    • アクションA3の実行と実行値3
    • アクションA1で実行するアクションA2で実行値6を持つ
    • 値7で実行されるアクションA3
    • アクションA3の値と実行8
    • アクションA2が値5
    • アクションA3の値と実行9
    • アクションA1 4
    • アクションA2値と実行値と実行10

    これを用いて実行期待どおり3つのActionBlock(s)の間でデータの分布を明確に示しています。

  • +0

    2番目のコンパイル例を取得できませんでした。 – Nathan

    4

    いいえ、いくつかの理由で2番目の例はコンパイルされません。実行ブロックではなく、「グループ化」データフローブロックに対してのみgreedy = falseを設定できます。 GroupingDataflowBlockOptions(DataflowBlockOptionsではなく)で設定する必要があります。コンストラクタパラメータではなくプロパティ値 "{Greedy = false}"に設定されます。

    アクションブロックの容量を絞るには、DataflowBlockOptionsのBoundedCapacityプロパティの値を設定します(OPが示すとおり、このオプションはすでに認識されていますが)。このように:

    var a1 = new ActionBlock<int>(
          i => doSomeWork(i), 
          new ExecutionDataflowBlockOptions {BoundedCapacity = 1} 
         ); 
    
    関連する問題