2012-10-08 4 views

risposta

15

Se tutto ciò che si vuole fare è inoltrare gli articoli da un blocco a molti altri, non è necessario BufferBlock.

Ma ci sono certamente casi in cui è utile. Ad esempio, se si dispone di una rete di flussi di dati complessa, è possibile che si desideri creare da sottoreti più piccole, ciascuna creata con il proprio metodo. E per fare questo, hai bisogno di un modo per rappresentare un gruppo di blocchi. Nel caso che hai menzionato, restituire il singolo BufferBlock (probabilmente come ITargetBlock) dal metodo sarebbe una soluzione semplice.

Un altro esempio in cui BufferBlock potrebbe essere utile se si desidera inviare elementi da diversi blocchi di origine a più blocchi di destinazione. Se si è utilizzato BufferBlock come intermediario, non è necessario collegare ciascun blocco di origine a ciascun blocco di destinazione.

Sono sicuro che ci sono molti altri esempi in cui è possibile utilizzare BufferBlock. Naturalmente, se non vedi alcun motivo per usarlo nel tuo caso, allora non farlo.

+0

Sento che usando BufferBlocks è il modo "pulito" di comunicare tra i blocchi di flusso di dati, ma è l'overhead (se presente) di utilizzare BufferBlocks vale la pena? – Dimitri

+1

Questo è per te da decidere. Se senti che rende il tuo codice più pulito, fallo. Ha qualche sovraccarico, ma penso che non dovrebbe essere evidente, a meno che non ti interessi davvero delle prestazioni. – svick

19

Per aggiungere alla risposta di svick, c'è un altro vantaggio dei bufferblock. Se hai un blocco con più link di output e vuoi bilanciarli tra loro, devi trasformare i blocchi di output in non-greedy e aggiungere un bufferblock per gestire l'accodamento. Ho trovato il seguente esempio utile:

Citato da un link che ora è morto:

Questo è ciò che stiamo progettando di fare:

  • Alcuni blocco di codice pubblicherà i dati al BufferBlock usando il suo Post (T t) metodo.
  • Questo BufferBlock è collegato a 3 istanze ActionBlock utilizzando il metodo LinkTo t) di BufferBlock.

nota, che BufferBlock non copie di handover dei dati d'ingresso a tutti i blocchi di destinazione esso è collegato to.Instead lo fa a un blocco di destinazione only.Here ci aspettiamo che, quando un obiettivo è occupato nell'elaborazione del request.It sarà consegnato all'altro target.Ora cerchiamo di fare riferimento alla seguente codice:

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

Quando eseguito produce il seguente output:

  • azione A1 esecuzione con valore 1
  • azione A1 esecuzione con il valore di 2
  • Azione A1 esecuzione con valore 3
  • Azione A1 esecuzione con valore 4
  • Azione A1 esecuzione con valore 5
  • Action A1 esecuzione con valore 6
  • Action A1 esecuzione con il valore 7
  • Action A1 esecuzione con valore 8
  • Action A1 esecuzione con valore 9
  • Action A1 esecuzione con il valore 10

Questo mostra che solo un obiettivo sta eseguendo tutti i dati anche quando è occupato (a causa del Thread.Sleep (100) aggiunto appositamente). Perché?

Questo perché tutti i blocchi di destinazione sono di default avidi di natura e bufferano l'input anche quando non sono in grado di elaborare i dati. Per modificare questo comportamento abbiamo impostato la proprietà Greedy su false in DataFlowBlockOptions durante l'inizializzazione del ActionBlock come mostrato di seguito.

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

L'output di questo programma è:

  • Action A1 esecuzione con valore 1
  • Action A2 esecuzione con valore 3
  • Action A1 esecuzione con valore 2
  • Azione A3 esecuzione con valore 6
  • Azione A3 in esecuzione con valore 7
  • Azione A3 esecuzione con valore 8
  • Action A2 esecuzione con valore 5
  • Azione A3 esecuzione con valore 9
  • Action A1 esecuzione con valore 4
  • Action A2 esecuzione con il valore 10

Questo chiaramente una distribuzione dei dati attraverso tre ActionBlock come previsto.

+0

Impossibile ottenere il secondo esempio da compilare. – Nathan

4

No, il secondo esempio non verrà compilato per una serie di motivi: È possibile impostare greedy = false solo per un blocco di flusso di dati "di raggruppamento" - non per un blocco di esecuzione; e quindi deve essere impostato tramite GroupingDataflowBlockOptions - non DataflowBlockOptions; e quindi viene impostato come valore di proprietà "{Greedy = false}" non come parametro del costruttore.

Se si desidera limitare la capacità di un blocco di azione, farlo impostando il valore della proprietà BoundedCapacity di DataflowBlockOptions (sebbene, come indicato dall'OP, siano già a conoscenza di questa opzione).Come questo:

var a1 = new ActionBlock<int>(
      i => doSomeWork(i), 
      new ExecutionDataflowBlockOptions {BoundedCapacity = 1} 
     ); 
Problemi correlati