2010-09-28 32 views
5

Chào buổi sáng! Giả sử chúng ta có lớp sau:Triển khai IDisposable cho lớp chứa chủ đề

class MultithreadOperation : IDisposable 
{ 
    private IList<Thread> operationThreads; 

    public void StartOperation() 
    { 
      // Initialize and start threads and put them to operationThreads 
    } 

    public void StopOperation() 
    { 
      // Aborts each thread. 
    } 

    public void Dispose() 
    { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposed) 
     { 
       disposed = true; 
       if (disposing) 
       { 
        // Release managed resources. 
        #1: 
        StopOperation(); 
       } 
       // Release unmanaged resources. 
       #2: 
       StopOperation(); 
     } 
    } 

    ~MultithreadOperation() 
    { 
     Dispose(false); 
    } 
} 

Thực ra, tôi cần dừng tất cả các chủ đề nếu cá thể được xử lý. Ngoài ra, tôi cần phải dừng tất cả các chủ đề nếu dụ là rác thu thập được (nếu không, các chủ đề sẽ vẫn còn sống, đó là xấu cho tôi). Chắc chắn, hoàn toàn hợp pháp khi gọi phương thức StopOperation() ở vị trí # 1.

Tôi muốn biết có bất kỳ cạm bẫy nào nếu chúng ta gọi StopOperation() ở vị trí số 2 không? Như tôi đã hiểu, danh sách các luồng có thể đã được thu thập rác khi ~ MultithreadOperation() đang thực hiện. Ngoài ra, tôi đã nhìn thấy rất nhiều khuyến nghị để tránh bất kỳ mã nào đề cập đến các tài nguyên được quản lý trong quá trình thực hiện Hoàn tất, đặc biệt là các trường mẫu.

Ngoài ra, sẽ rất thú vị khi nghe về các cách tiếp cận khác nhau cho vấn đề này. Cảm ơn!

+0

có thể trùng lặp của mẫu [C# Finalize/Dispose pattern] (http://stackoverflow.com/questions/898828/c-finalize-dispose-pattern) – x0n

+0

bỏ phiếu để đóng. điều này đã được yêu cầu và trả lời hàng triệu lần trên web. thay thế "chuỗi" bằng "đối tượng được quản lý" và câu hỏi là trùng lặp. – x0n

+0

Làm thế nào về chủ đề sẽ vẫn còn sống sau khi MutilthreadOperation là rác thu thập? Hành vi này khá tệ. Chủ đề Bạn đã gợi ý không dừng thread trong finalizer. – Alexander

Trả lời

5

Điều này hoàn toàn không phù hợp. Một người sử dụng của lớp học của bạn mà không biết các chi tiết tốt sẽ giả định rằng gọi Dispose() sẽ làm điều gì đó vô tội. Một chút dọn dẹp, không có gì lạ mắt. Cô sẽ không mong đợi chương trình sẽ bị bỏ lại ở trạng thái hoàn toàn không thể đoán trước với các chủ đề biến đổi trạng thái đó bị hủy bỏ tại các vị trí ngẫu nhiên mà không có cách nào để khôi phục trạng thái.

Nó cũng hoàn thành redrum trên thread finalizer, hủy bỏ một thread có thể mất nhiều giây nếu thread không ở trong trạng thái có thể cảnh báo. CLR sẽ hủy bỏ chuỗi finalizer sau 2 giây và chấm dứt chương trình, không đưa ra chẩn đoán nào về lý do thực sự của chương trình bị lỗi.

+0

Cảm ơn. Điều này làm cho tình hình rõ ràng. – Alexander

+0

Đoán tôi hơi muộn ở đây, nhưng những gì về khi các chủ đề đang được thoát sạch trong StopOperation()? Hơn nữa, những trường hợp Vứt bỏ không được gọi là gì? –

2

Nó không phải là an toàn để đặt một cuộc gọi đến StopOperation ở vị trí thứ 2 vì:

  • Các List của chủ đề hoặc Thread trường hợp mình có thể đã được thu thập.
  • Không rõ ràng nếu các khối StopOperation của bạn cho đến khi tất cả các luồng đã xác nhận tín hiệu dừng, nhưng nếu có thì nó có thể treo lên chuỗi kết thúc.

Một điểm khác là bạn không nên hủy các chủ đề (qua Thread.Abort) trong trường hợp này. Lý do là vì việc hủy bỏ tiêm một ngoại lệ out-of-band (không đồng bộ) vào luồng đích ở các điểm không xác định. Bởi vì các điểm tiêm này là ngẫu nhiên (và thường không trực quan) nó có thể làm hỏng trạng thái (bằng cách bailing ra sớm ở giữa viết hoặc hoạt động phức tạp hơn) trong miền ứng dụng hoặc thậm chí toàn bộ quá trình.

Cách tốt nhất để xử lý này là để làm như sau:

  • Có thăm dò chủ đề hoặc nghe cho một tín hiệu dừng lại. Bạn có thể xem my answer here để biết các mẹo về cách thực hiện việc này.
  • Có tín hiệu StopOperation bất kỳ cơ chế dừng nào bạn đã chọn. Thông thường bạn muốn đợi cho đến khi tất cả các chủ đề đã nhận được tín hiệu và tự tắt máy. Bạn có thể đợi một chuỗi bằng cách gọi Thread.Join.
  • Gọi StopOperation theo phương thức Dispose ở vị trí số 1.

Bây giờ, làm thế nào để chúng ta đối phó với những người gọi không chơi tốt và tránh gọi Dispose? Điều này phức tạp hơn một chút. Tôi thấy hai mẫu chung để xử lý vấn đề này.

  • Có trình kết thúc bật cờ dừng. Cờ đó phải là một loại giá trị nếu không nó sẽ bị thu gom rác. Mỗi luồng phải thăm dò ý kiến ​​cờ này theo định kỳ. Điều đó có nghĩa là bạn không nên thực hiện bất kỳ cuộc gọi chặn nào chờ đợi vô thời hạn. Hãy nhớ rằng, bạn không thể gọi số Thread.Interrupt để chọc luồng vì yêu cầu tham chiếu đến phiên bản Thread.
  • Duy trì danh sách các chuỗi đang hoạt động trong biến thu thập tĩnh theo cách bạn có thể liệt kê an toàn chúng trong trình hoàn tất. Tuy nhiên, tôi phải thừa nhận rằng tôi ít gỉ về các quy tắc thu thập liên quan đến tham chiếu tĩnh trong quá trình tải miền ứng dụng (ví dụ: việc tắt ứng dụng). Vì vậy, tôi không chắc chắn rằng nó là hoàn toàn an toàn.
Các vấn đề liên quan