2010-08-04 7 views
11

Tôi có một ứng dụng đa luồng để sinh ra các chủ đề cho một số công cụ phần cứng. Mỗi luồng về cơ bản là một vòng lặp vô hạn (cho toàn bộ thời gian của ứng dụng) thăm dò phần cứng cho dữ liệu mới, và kích hoạt một sự kiện (mà truyền dữ liệu) mỗi khi nó thu thập một cái gì đó mới. Có một lớp người nghe duy nhất hợp nhất tất cả các công cụ này, thực hiện một số phép tính và kích hoạt một sự kiện mới với phép tính này.Sự kiện so với Lợi nhuận

Tuy nhiên, tôi tự hỏi, vì có một người nghe duy nhất, sẽ tốt hơn để vạch ra phương pháp IEnumerable<> tắt các công cụ này và sử dụng yield return để trả lại dữ liệu thay vì kích hoạt sự kiện.

Tôi muốn xem nếu ai biết sự khác biệt trong hai phương pháp này. Đặc biệt, tôi đang tìm kiếm độ tin cậy tốt nhất, khả năng tốt nhất để tạm dừng/hủy hoạt động, tốt nhất cho mục đích luồng, an toàn chung, v.v.

Ngoài ra, với phương pháp thứ hai là vẫn có thể chạy vòng lặp IEnumerable một chủ đề riêng biệt? Nhiều người trong số các công cụ này có phần nào bị ràng buộc CPU, vì vậy đảm bảo mỗi công cụ trên một chủ đề khác nhau là rất quan trọng.

Trả lời

6

này nghe có vẻ giống như một trường hợp sử dụng rất tốt cho việc mở rộng hoạt tính. Có một chút của một đường cong học tập với nó nhưng trong một nutshell, IObservable là dual của IEnumerable. Khi IEnumerable yêu cầu bạn kéo từ nó, IObservable đẩy giá trị của nó tới người quan sát. Khá nhiều thời gian bạn cần để chặn trong điều tra viên của bạn, đó là một dấu hiệu tốt, bạn nên đảo ngược mô hình và sử dụng một mô hình đẩy. Sự kiện là một cách để đi nhưng IObservable có nhiều tính linh hoạt hơn vì nó có thể tổng hợp và nhận biết luồng.

instrument.DataEvents 
      .Where(x => x.SomeProperty == something) 
      .BufferWithTime(TimeSpan.FromSeconds(1)) 
      .Subscribe(x => DoSomethingWith(x)); 

Trong ví dụ trên, DoSomethingWith (x) sẽ được gọi bất cứ khi nào đối tượng (công cụ) tạo ra một DataEvent mà có một SomeProperty phù hợp và nó đệm các sự kiện vào đợt 1 khoảng thời gian thứ hai.

Có rất nhiều thứ bạn có thể làm như sáp nhập trong các sự kiện do các chủ đề khác tạo ra hoặc chuyển hướng thông báo lên chuỗi giao diện người dùng, v.v.Thật không may tài liệu hiện tại khá yếu nhưng có một số thông tin tốt trên Matthew Podwysocki's blog. (Mặc dù các bài đăng của ông hầu như chỉ đề cập đến Tiện ích mở rộng phản ứng cho JavaScript, nhưng cũng hoàn toàn có thể áp dụng cho Tiện ích mở rộng phản ứng cho .NET.)

+0

Tôi đã sử dụng Rx trên một dự án mới và thật tuyệt vời. Vấn đề trong trường hợp này là nó sẽ đòi hỏi một đại tu chính của mã di sản, trong đó nó thay đổi toàn bộ cách vấn đề thậm chí còn tiếp cận. Đối với phiên bản chính tiếp theo, tôi đã làm việc này, nhưng nó có vẻ như một nhiệm vụ khó khăn cho một thiết kế lại nhỏ (er). – drharris

4

Đó là một cuộc gọi gần, nhưng tôi nghĩ rằng tôi muốn dính vào mô hình sự kiện trong trường hợp này, với người quyết định chính behing mà các lập trình viên bảo trì trong tương lai là ít có khả năng hiểu được khái niệm năng suất. Ngoài ra, sản lượng có nghĩa là việc xử lý mã mỗi yêu cầu phần cứng nằm trong cùng một luồng với mã tạo ra các yêu cầu xử lý. Đó là xấu, bởi vì nó có thể có nghĩa là phần cứng của bạn phải chờ đợi trên mã người tiêu dùng.

Và nói của người tiêu dùng, tùy chọn khác là một hàng đợi nhà sản xuất/tiêu dùng. Dụng cụ của bạn tất cả có thể đẩy vào cùng một hàng đợi và người nghe duy nhất của bạn sau đó có thể bật từ nó làm bất cứ điều gì từ đó.

+0

Tôi không quá lo lắng về khả năng bảo trì. Vì đây là phần quan trọng trong nhiệm vụ của ứng dụng nên nó được viết thành tài liệu mà những người không lập trình có thể tìm được cách của họ. Cảm ơn bạn đã trả lời câu hỏi về luồng. Và, con trỏ tốt về việc sử dụng hàng đợi. Tôi đang nhìn vào lớp 'ConcurrentQueue 'mới và có thể sẽ đi lên tuyến đường này. – drharris

1

Stephen Toub blogs về hàng đợi chặn thực hiện IEnumerable dưới dạng vòng lặp vô hạn bằng cách sử dụng từ khóa yield. Chủ đề công nhân của bạn có thể enqueue điểm dữ liệu mới khi chúng xuất hiện và các chủ đề tính toán có thể dequeue chúng bằng cách sử dụng một vòng lặp foreach với ngữ nghĩa chặn.

2

Có sự khác biệt khá cơ bản, đẩy vs pull. Mô hình kéo (sản lượng) là mô hình khó thực hiện hơn từ giao diện thiết bị. Bởi vì bạn sẽ phải lưu trữ dữ liệu cho đến khi mã máy khách sẵn sàng để kéo. Khi bạn đẩy, khách hàng có thể hoặc không thể lưu trữ, khi xét thấy cần thiết.

Nhưng thực tế hầu hết các trường trong các kịch bản đa luồng cần để đối phó với chi phí trong tất yếu chuyển đổi bối cảnh thread đó là cần thiết để trình bày dữ liệu. Và điều đó thường được thực hiện bằng cách kéo, sử dụng hàng đợi được chặn an toàn.

0

Tôi không nghĩ có sự khác biệt về hiệu suất giữa sự kiện và cách tiếp cận yield. Yield được đánh giá lười biếng, do đó, nó để lại một cơ hội để báo hiệu các chủ đề sản xuất để dừng lại. Nếu mã của bạn là tài liệu chu đáo thì bảo trì cũng phải là một sự rửa.

Tùy chọn của tôi là tùy chọn thứ ba, để sử dụng phương thức gọi lại thay vì sự kiện (mặc dù cả hai đều liên quan đến đại biểu). Các nhà sản xuất của bạn gọi lại cuộc gọi lại mỗi khi họ có dữ liệu. Các cuộc gọi lại có thể trả về các giá trị, vì vậy người tiêu dùng của bạn có thể báo hiệu các nhà sản xuất ngừng hoặc tiếp tục mỗi khi họ đăng ký dữ liệu.

Cách tiếp cận này có thể cung cấp cho bạn các địa điểm để tối ưu hóa hiệu suất nếu bạn có khối lượng dữ liệu cao. Trong callback của bạn, bạn khóa trên một đối tượng trung lập và nối thêm dữ liệu vào bộ sưu tập. Thời gian chạy nội bộ sử dụng hàng đợi sẵn sàng trên đối tượng khóa, do đó, điều này có thể đóng vai trò là điểm xếp hàng của bạn.

Điều này cho phép bạn chọn một bộ sưu tập, chẳng hạn như List<T> với dung lượng được xác định trước, đó là O (1) để thêm vào. Bạn cũng có thể tăng gấp đôi bộ đệm người tiêu dùng của bạn, với gọi lại của bạn phụ thêm vào bộ đệm "trái" trong khi bạn hợp nhất từ ​​"bên phải", v.v. Điều này giảm thiểu số lượng chặn nhà sản xuất và dữ liệu bị bỏ sót liên quan, điều này rất tiện lợi cho dữ liệu bùng nổ. Bạn cũng có thể dễ dàng đo lường các dấu hiệu và tỷ lệ xử lý nước cao khi bạn thay đổi số lượng chủ đề.