2012-10-08 17 views

Trả lời

15

Nếu tất cả những gì bạn muốn làm là chuyển tiếp các mục từ một khối này sang một khối khác, bạn không cần BufferBlock.

Nhưng chắc chắn có những trường hợp hữu ích. Ví dụ, nếu bạn có một mạng dữ liệu phức tạp, bạn có thể muốn xây dựng nó từ các mạng con nhỏ hơn, mỗi mạng được tạo theo phương pháp riêng của nó. Và để làm điều này, bạn cần một số cách để đại diện cho một nhóm các khối. Trong trường hợp bạn đề cập, hãy trả lại đơn BufferBlock (có thể là ITargetBlock) từ phương pháp này sẽ là giải pháp dễ dàng.

Ví dụ khác trong đó BufferBlock sẽ hữu ích là nếu bạn muốn gửi các mục từ nhiều khối nguồn tới một số khối mục tiêu. Nếu bạn đã sử dụng BufferBlock làm người trung gian, bạn không phải kết nối từng khối nguồn với từng khối mục tiêu.

Tôi chắc chắn có nhiều ví dụ khác mà bạn có thể sử dụng BufferBlock. Tất nhiên, nếu bạn không thấy bất kỳ lý do gì để sử dụng nó trong trường hợp của bạn, thì không.

+0

Tôi cảm thấy rằng việc sử dụng BufferBlocks là cách "sạch" hơn trong giao tiếp giữa các khối dữ liệu, nhưng chi phí (nếu có) của việc sử dụng BufferBlocks có đáng không? – Dimitri

+1

Đó là để bạn quyết định. Nếu bạn cảm thấy nó làm cho mã của bạn sạch hơn, hãy làm điều đó. Nó có một số chi phí, nhưng tôi nghĩ rằng không nên chú ý, trừ khi bạn thực sự quan tâm đến hiệu suất. – svick

19

Để thêm vào câu trả lời của svick, có một lợi ích khác của bộ đệm. Nếu bạn có một khối với nhiều liên kết đầu ra và muốn cân bằng giữa chúng, bạn phải biến các khối đầu ra thành không tham lam và thêm một bufferblock để xử lý hàng đợi. Tôi tìm thấy ví dụ sau đây hữu ích:

Trích dẫn từ một liên kết mà bây giờ đã chết:

Đây là những gì chúng tôi đang có kế hoạch để làm:

  • Một số khối mã sẽ gửi dữ liệu đến BufferBlock sử dụng nó Post (T t) phương pháp.
  • BufferBlock này được liên kết với 3 trường hợp ActionBlock bằng cách sử dụng phương thức LinkTo t) của BufferBlock.

Lưu ý rằng BufferBlock không chuyển giao các bản sao của dữ liệu đầu vào cho tất cả các khối mục tiêu mà nó được liên kết đến.Instead nó chỉ thực hiện với một khối đích. Chúng tôi hy vọng rằng khi một mục tiêu bận xử lý yêu cầu. Nó sẽ được bàn giao cho mục tiêu khác.Bây giờ chúng ta hãy tham khảo các mã bên dưới:

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

Khi thực hiện nó tạo ra kết quả như sau:

  • Action A1 thực hiện với giá trị 1
  • Action A1 thực hiện với giá trị 2
  • Action A1 thi với giá trị 3
  • Thực hiện hành động A1 với giá trị 4
  • Thực hiện hành động A1 với giá trị 5
  • Action A1 thực hiện với giá trị 6
  • Action A1 thực hiện với giá trị 7
  • Action A1 thực hiện với giá trị 8
  • Action A1 thực hiện với giá trị 9
  • Action A1 thực hiện với giá trị 10

Điều này cho thấy chỉ có một mục tiêu thực sự thực thi tất cả dữ liệu ngay cả khi nó bận (do Thread.Sleep (100) được thêm một cách có chủ ý) .Tại sao?

Điều này là do tất cả các khối mục tiêu theo mặc định tham lam trong tự nhiên và đệm đầu vào ngay cả khi chúng không thể xử lý dữ liệu.Để thay đổi hành vi này, chúng tôi đã đặt thuộc tính tham lam thành false trong DataFlowBlockOptions khi khởi tạo ActionBlock như hình dưới đây.

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

Kết quả của chương trình này là:

  • Action A1 thực hiện với giá trị 1
  • Action A2 thực hiện với giá trị 3
  • Action A1 thực hiện với giá trị 2
  • Action A3 thực hiện với giá trị 6
  • Thực thi hành động A3 với giá trị 7
  • Action A3 thực hiện với giá trị 8
  • Action A2 thực hiện với giá trị 5
  • Action A3 thực hiện với giá trị 9
  • Action A1 thực hiện với giá trị 4
  • Action A2 thực hiện với giá trị 10

này phân phối rõ ràng dữ liệu trên ba ActionBlock (s) như mong đợi.

+0

Không thể lấy ví dụ thứ hai để biên dịch. – Nathan

4

Không, ví dụ thứ hai sẽ không biên dịch vì một số lý do: Chỉ có thể đặt tham lam = false cho khối nhóm dữ liệu "nhóm" - không phải cho khối thực thi; và sau đó nó phải được thiết lập thông qua GroupingDataflowBlockOptions - không phải DataflowBlockOptions; và sau đó nó được thiết lập như một giá trị tài sản "{Greedy = false}" không phải là một tham số hàm tạo.

Nếu bạn muốn tăng tốc công suất của khối hành động, hãy thực hiện bằng cách đặt giá trị của thuộc tính BoundedCapacity của DataflowBlockOptions (mặc dù OP được nêu, họ đã biết tùy chọn này).Như thế này:

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