2008-09-28 41 views
6

Lời chào.Thực hiện đa luồng trong C# (xem xét mã)

Tôi đang cố triển khai một số mã đa luồng trong một ứng dụng. Mục đích của mã này là để xác nhận các mục mà cơ sở dữ liệu cung cấp cho nó. Quá trình xác nhận có thể mất nhiều thời gian (vài trăm giây đến vài giây), vì vậy quá trình này cần phải được chia nhỏ thành chuỗi riêng cho mỗi mục.

Cơ sở dữ liệu có thể cung cấp 20 hoặc 30 mục trong giây, nhưng bắt đầu giảm nhanh chóng, cuối cùng đạt khoảng 65 nghìn mục trong 24 giờ, tại thời điểm đó ứng dụng thoát.

Tôi muốn nó nếu có ai hiểu biết hơn có thể xem nhanh mã của tôi và xem liệu có bất kỳ vấn đề rõ ràng nào không. Không có ai tôi làm việc với biết đa luồng, vì vậy tôi thực sự chỉ trên của riêng tôi, trên này.

Đây là mã. It's kinda dài nhưng nên được khá rõ ràng. Hãy cho tôi biết nếu bạn có bất kỳ phản hồi hoặc lời khuyên nào. Cảm ơn!

public class ItemValidationService 
{ 
    /// <summary> 
    /// The object to lock on in this class, for multithreading purposes. 
    /// </summary> 
    private static object locker = new object(); 

    /// <summary>Items that have been validated.</summary> 
    private HashSet<int> validatedItems; 

    /// <summary>Items that are currently being validated.</summary> 
    private HashSet<int> validatingItems; 

    /// <summary>Remove an item from the index if its links are bad.</summary> 
    /// <param name="id">The ID of the item.</param> 
    public void ValidateItem(int id) 
    { 
     lock (locker) 
     { 
      if 
      (
       !this.validatedItems.Contains(id) && 
       !this.validatingItems.Contains(id) 
      ){ 
       ThreadPool.QueueUserWorkItem(sender => 
       { 
        this.Validate(id); 
       }); 
      } 
     } 

    } // method 

    private void Validate(int itemId) 
    { 
     lock (locker) 
     { 
      this.validatingItems.Add(itemId); 
     } 

     // ********************************************* 
     // Time-consuming routine to validate an item... 
     // ********************************************* 

     lock (locker) 
     { 
      this.validatingItems.Remove(itemId); 
      this.validatedItems.Add(itemId); 
     } 

    } // method 

} // class 
+0

@Chris: Tại sao 'locker' tĩnh nếu các biến thành viên được bảo vệ bởi 'locker' không? 'locker' nên là một biến thành viên chứ không phải là một biến lớp. – user7116

Trả lời

4

Các chủ đề hồ bơi là một lựa chọn tiện lợi nếu bạn có nhẹ cân xử lý lẻ tẻ đó không phải là thời điểm nhạy cảm. Tuy nhiên, tôi nhớ lại đọc trên MSDN rằng nó không thích hợp cho việc xử lý quy mô lớn về bản chất này.

Tôi đã sử dụng nó cho một cái gì đó khá giống với điều này và hối tiếc. Tôi đã tiếp cận công nhân trong các ứng dụng tiếp theo và hạnh phúc hơn nhiều với mức độ kiểm soát mà tôi có.

Mẫu yêu thích của tôi trong mô hình chuỗi công việc là tạo một chuỗi chủ chứa hàng đợi các mục tác vụ. Sau đó, ngã ba một loạt các công nhân mà bật các mục ra khỏi hàng đợi đó để xử lý. Tôi sử dụng hàng đợi chặn để khi không có mục nào trong quá trình, công nhân chỉ chặn cho đến khi có thứ gì đó được đẩy lên hàng đợi. Trong mô hình này, luồng chủ tạo ra các mục công việc từ một số nguồn (db, vv) và các luồng công nhân tiêu thụ chúng.

0

Tôi sẽ quan tâm đến hiệu suất tại đây. Bạn chỉ ra rằng cơ sở dữ liệu có thể cung cấp cho nó 20-30 mục mỗi giây và một mục có thể mất đến vài giây để được xác nhận. Đó có thể là một số lượng lớn các chủ đề - sử dụng các số liệu của bạn, trường hợp xấu nhất 60-90 chủ đề! Tôi nghĩ bạn cần xem xét lại thiết kế ở đây. Michael đã đề cập một mô hình tốt đẹp. Việc sử dụng hàng đợi thực sự giúp giữ cho mọi thứ được kiểm soát và tổ chức. Một semaphore cũng có thể được sử dụng để kiểm soát số lượng các luồng được tạo ra - nghĩa là bạn có thể có số lượng chuỗi tối đa cho phép, nhưng với tải nhỏ hơn, bạn sẽ không nhất thiết phải tạo số tối đa nếu ít kết thúc hơn khi hoàn thành công việc - - tức là kích thước hồ bơi của riêng bạn có thể năng động với nắp.

Khi sử dụng nhóm chủ đề, tôi cũng thấy khó theo dõi việc thực hiện các chuỗi từ hồ bơi khi thực hiện tác phẩm. Vì vậy, trừ khi nó cháy và quên, tôi ủng hộ thực hiện kiểm soát nhiều hơn. Tôi biết bạn đã đề cập rằng ứng dụng của bạn thoát sau khi tất cả các mục 65K đều đã hoàn tất. Làm thế nào bạn theo dõi bạn chủ đề để xác định xem họ đã hoàn thành công việc của họ - tức là tất cả các công nhân xếp hàng được thực hiện. Bạn có đang theo dõi trạng thái của tất cả các mục trong HashSets không? Tôi nghĩ rằng bằng cách xếp hàng của bạn lên và có chủ đề công nhân của riêng bạn tiêu thụ hàng đợi đó, bạn có thể giành quyền kiểm soát nhiều hơn. Mặc dù vậy, điều này có thể đi kèm với chi phí của chi phí cao hơn trong điều khoản của tín hiệu giữa các chủ đề để cho biết khi tất cả các mục đã được xếp hàng đợi cho phép chúng thoát ra.

+0

Tôi đang thêm từng mục vào ThreadPool để cho phép có bao nhiêu chuỗi thực sự chạy cùng một lúc. Tôi không chỉ tạo một chủ đề mới cho mỗi mục và bắt đầu nó. – core

+0

Điểm tốt. Tôi sẽ chỉ được quan tâm về maxing ra threadpool kể từ khi nó được sử dụng ở nơi khác của CLR; tuy nhiên, nếu tất cả ứng dụng này đang hoạt động ... –

+0

Điều duy nhất tôi sẽ đưa ra là, bạn có biết nếu QueueUserWorkItem có thể xử lý xếp hàng một số lượng lớn các mục? Nó có giới hạn không? Điều gì sẽ xảy ra nếu QueueUserWorkItem trả về false? –

2

Tôi thứ hai ý tưởng sử dụng một hàng đợi chặn và đề người lao động.Đây là triển khai hàng đợi chặn mà tôi đã sử dụng trong quá khứ với kết quả tốt: http://www.codeproject.com/KB/recipes/boundedblockingqueue.aspx

Điều gì liên quan đến logic xác thực của bạn? Nếu nó chủ yếu là CPU bị ràng buộc sau đó tôi sẽ tạo ra không quá 1 thread công nhân cho mỗi bộ xử lý/lõi trên hộp. Điều này sẽ cho bạn biết số lượng bộ vi xử lý: Environment.ProcessorCount

Nếu xác thực của bạn liên quan đến I/O như truy cập tệp hoặc truy cập cơ sở dữ liệu thì bạn có thể sử dụng một vài chủ đề hơn số lượng bộ xử lý.

+0

Tham khảo tốt - Tôi đã sử dụng cùng một triển khai trong một nhà sản xuất đơn lẻ/nhiều kịch bản tiêu dùng. Tôi sửa đổi nó một thời gian là tốt, biến nó thành một hàng đợi byte (ít hơn với các loại giá trị boxing/unboxing) để làm việc xử lý tập tin lớn - làm việc tuyệt vời. Thực hiện rất chắc chắn. –

1

Có lỗi logic có thể xảy ra trong mã được đăng với câu hỏi, tùy thuộc vào nơi id mục trong số ValidateItem(int id) xuất phát từ đó. Tại sao? Bởi vì mặc dù bạn khóa chính xác hàng đợi validatingItems và validatedItems của bạn trước khi xếp hàng một mục công việc, bạn không thêm mục đó vào hàng đợi validatingItems cho đến khi chuỗi mới quay lên. Điều đó có nghĩa là có thể có khoảng cách thời gian mà một chuỗi khác gọi là ValidateItem(id) với cùng một id (trừ khi điều này đang chạy trên một chuỗi chính).

Tôi sẽ thêm mục vào hàng đợi validatingItems ngay trước khi xếp hàng, bên trong khóa.

Chỉnh sửa: cũng QueueUserWorkItem() trả về một bool để bạn nên sử dụng giá trị trả về để đảm bảo mục được xếp hàng và THEN thêm nó vào hàng đợi validatingItems.

+0

chỉnh sửa để chỉnh sửa của bạn, mà giới thiệu lại các lỗi. bạn cần phải thêm nó, sau đó xếp hàng, sau đó loại bỏ nó nếu xếp hàng thất bại. – TheSoftwareJedi

1

ThreadPool có thể không được tối ưu để gây nhiễu quá nhiều vào cùng một lúc. Bạn có thể muốn nghiên cứu các giới hạn trên của khả năng của nó và/hoặc cuộn của riêng bạn.

Ngoài ra, có một điều kiện chủng tộc tồn tại trong mã của bạn, nếu bạn không mong đợi xác thực trùng lặp. Các cuộc gọi đến

this.validatingItems.Add(itemId); 

cần phải xảy ra trong thread chính (ValidateItem), không phải trong thread bơi (Validate phương pháp). Cuộc gọi này sẽ xảy ra một dòng trước khi xếp hàng mục công việc vào nhóm.

Một lỗi tồi tệ hơn được tìm thấy bằng cách không kiểm tra sự trở lại của QueueUserWorkItem. Xếp hàng có thể thất bại, và tại sao nó không ném một ngoại lệ là một bí ẩn đối với tất cả chúng ta. Nếu nó trả về false, bạn cần phải loại bỏ mục đã được thêm vào danh sách validatingItems, và xử lý lỗi (có thể thực thi lệnh ném).

0

Bạn cũng có thể thử sử dụng CCR - Thời gian chạy đồng thời và phối hợp. Nó được chôn bên trong Microsoft Robotics Studio, nhưng cung cấp một API tuyệt vời để làm điều này.

Bạn chỉ cần tạo một "Cổng" (về bản chất là hàng đợi), kết nối một bộ tiếp nhận (phương thức được gọi khi có gì đó được đăng lên), sau đó đăng các mục công việc lên nó. CCR xử lý hàng đợi và chuỗi công nhân để chạy nó.

Here's a video on Channel9 about the CCR.

Nó rất cao hiệu suất và thậm chí còn được sử dụng cho các phi Robotics thứ (Myspace.com sử dụng nó đằng sau scenese cho mạng nội dung giao của họ).

Các vấn đề liên quan