2013-08-28 45 views
13

Tôi có một lớp Class tạo ra một Thread trong hàm tạo của nó. Chủ đề này chạy một vòng lặp while(true) đang đọc dữ liệu không quan trọng từ một số NetStream. Các chủ đề sẽ được hủy bỏ bởi destructor:Destructor không bao giờ được gọi là

~Class() 
{ 
_thread.Abort(); 
_thread = null; 
} 

Khi chương trình muốn chấm dứt việc sử dụng các ví dụ Class 's - ClassInstance, nó gọi:

ClassInstance = null; 
GC.Collect; 

Tôi nghĩ đó có nghĩa là ~Class() sẽ người gọi tự động vào thời điểm đó - nhưng không phải.

Chuỗi này tiếp tục chạy ngay cả sau Application.Exit() và quay lại từ Main().

+16

Trình hủy lớp C# [không được đảm bảo để được gọi] (http://blogs.msdn.com/b/ericlippert/archive/2010/01/21/what-s-the-difference-between-a- destructor-and-a-finalizer.aspx). Sử dụng [Mẫu vứt bỏ] (http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx) để xóa tài nguyên xác định. – Romoku

+2

Thay vì gọi GC.Collect, sử dụng câu lệnh using() http://msdn.microsoft.com/en-us/library/yh598w02.aspx Lời khuyên chung là bạn không nên gọi GC.Collect -> http: // stackoverflow.com/questions/478167/when-is-it-acceptable-to-call-gc-collect – Oliver

+0

Đặt một biến mẫu thành 'null' là * không giống * như gọi hàm hủy! Trên thực tế nó hầu như không ảnh hưởng đến cá thể đối tượng, chỉ có một tham chiếu ít hơn. –

Trả lời

7

Mã quan trọng của mã không được bao gồm; cách bắt đầu luồng và phương thức nó đang chạy. Nếu tôi phải đoán, tôi có thể nói rằng có thể bạn đã bắt đầu luồng bằng cách chuyển một phương thức thể hiện của Class. Vì vậy, về cơ bản trường hợp lớp của bạn vẫn còn bắt nguồn từ việc chạy chuỗi. Bạn cố gắng để ngăn chặn các chủ đề trong finalizer, nhưng finalizer sẽ không bao giờ chạy vì dụ vẫn còn bắt nguồn từ dẫn đến một tình huống catch-22.

Ngoài ra, bạn đã đề cập rằng chuỗi đang chạy mã không quan trọng và đó là lý do của bạn để sử dụng Thread.Abort. Đó thực sự không phải là một lý do đủ tốt. Rất khó để kiểm soát nơi mà ThreadAbortException sẽ được tiêm vào luồng và kết quả là nó có thể làm hỏng cấu trúc dữ liệu chương trình quan trọng mà bạn không lường trước được.

Sử dụng cơ chế cooperative cancellation mới đi kèm với TPL. Thay vào đó, hãy thay đổi vòng lặp while (true) để thăm dò ý kiến ​​số CancellationToken. Báo hiệu hủy trong phương thức Dispose khi bạn triển khai IDisposable. Không bao gồm một finalizer (destructor trong C# thuật ngữ). Finalizers được dự định sẽ được sử dụng để làm sạch tài nguyên không được quản lý. Vì bạn đã không chỉ ra rằng các tài nguyên không được quản lý đang được chơi thì việc vô hiệu hóa sẽ là vô nghĩa. Bạn không cần phải bao gồm một finalizer khi thực hiện IDisposable. Trong thực tế, nó được coi là thực hành xấu để có một khi nó không thực sự cần thiết.

public class Class : IDisposable 
{ 
    private Task task; 
    private CancellationTokenSource cts = new CancellationTokenSource(); 

    Class() 
    { 
    task = new Task(Run, cts.Token, TaskCreationOptions.LongRunning); 
    task.Start(); 
    } 

    public void Dispose() 
    { 
    cts.Cancel(); 
    } 

    private void Run() 
    { 
    while (!cts.Token.IsCancellationRequested) 
    { 
     // Your stuff goes here. 
    } 
    } 
} 
+0

Gần đây tôi đã chuyển một số mã trong GUI sử dụng 'BackgroundWorker' để sử dụng' TPL' và rất ấn tượng. – bhs

5

Nếu bạn triển khai IDisposable và bỏ đối tượng, thì mã trong Vứt bỏ sẽ chạy, nhưng không đảm bảo rằng Trình hủy cũng sẽ được gọi.

Bộ thu rác tạo thành ý kiến ​​cho rằng đó là một sự lãng phí thời gian. Vì vậy, nếu bạn muốn có một xử lý có thể dự đoán, bạn có thể sử dụng IDisposable.

Kiểm tra điều này Thread

2

CLR duy trì tất cả các chuỗi đang chạy. Bạn sẽ vượt qua số InstanceMethod của lớp học của bạn cho người xây dựng của chuỗi với tư cách là ThreadStart hoặc ParameterizedThreadStart đại biểu. Delegate sẽ giữ MethodInfo của phương thức bạn đã vượt qua và Instance của lớp học của bạn trong Target Thuộc tính.

Bộ thu gom rác và đối tượng không được có bất kỳ Strong References nhưng bản sao của bạn vẫn còn sống bên trong Delegate của Thread. Vì vậy, lớp học của bạn vẫn còn có Strong Reference do đó nó không đủ điều kiện để thu gom rác thải.

Để chứng minh những gì tôi đã nêu ở trên

public class Program 
{ 
    [STAThread] 
    static void Main(string[] args) 
    { 
     GcTest(); 

     Console.Read(); 
    } 

    private static void GcTest() 
    { 
     Class cls = new Class(); 
     Thread.Sleep(10); 
     cls = null; 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
    } 
} 

public class Class 
{ 
    private Thread _thread; 
    ~Class() 
    { 
     Console.WriteLine("~Class"); 
     _thread.Abort(); 
     _thread = null; 
    } 

    public Class() 
    { 
     _thread = new Thread(ThreadProc); 
     _thread.Start(); 
    } 

    private void ThreadProc() 
    { 
     while (true) 
     { 
      Thread.Sleep(10); 
     } 
    } 
} 

}

Hãy thử đoạn code trên. Destructor Sẽ không được gọi. Để làm cho nó hoạt đánh dấu phương pháp ThreadProc như static và chạy lại Destructorsẽ được gọi

2

Hơi off-topic: Bạn có thể sử dụng Tasks thay vì đề khỏa thân để chạy chức năng mà không lo lắng về việc xử lý.

Có nhiều vấn đề ở đây:

  • Thiết lập biến thành vô giá trị không xóa bất cứ điều gì, nó chỉ đơn giản loại bỏ một tham chiếu đến thể hiện của bạn.
  • Trình phá hủy sẽ chỉ được gọi khi bộ thu gom rác quyết định thu thập bản sao của bạn. Bộ thu gom rác không thường xuyên chạy, thường chỉ khi phát hiện có áp lực bộ nhớ.
  • Bộ thu gom rác thu thập các bộ sưu tập mồ côi CHỈ. Trẻ mồ côi có nghĩa là bất kỳ tài liệu tham khảo nào được chỉ định bởi đối tượng của bạn đều không hợp lệ.

Bạn nên triển khai giao diện IDisposable và gọi bất kỳ mã dọn dẹp nào trong phương thức Vứt bỏ. C# và VB cung cấp từ khóa using để dễ dàng xử lý ngay cả khi đối mặt với ngoại lệ.

Một thực hiện IDisposable điển hình là tương tự như sau:

class MyClass:IDisposable 
{ 
    ClassB _otherClass; 

    ... 


    ~MyClass() 
    { 
     //Call Dispose from constructor 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     //Call Dispose Explicitly 
     Dispose(true); 
     //Tell the GC not call our destructor, we already cleaned the object ourselves 
     GC.SuppressFinalize(this); 
    } 

    protected virtual Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      //Clean up MANAGED resources here. These are guaranteed to be INvalid if 
      //Dispose gets called by the constructor 

      //Clean this if it is an IDisposable 
      _otherClass.Dispose(); 

      //Make sure to release our reference 
      _otherClass=null; 
     } 
     //Clean UNMANAGED resources here 
    } 
} 

Sau đó, bạn có thể sử dụng lớp học của bạn như thế này:

using(var myClass=new MyClass()) 
{ 
    ... 
} 

Khi using chấm dứt khối, Dispose() sẽ được gọi là thậm chí nếu một ngoại lệ xảy ra.

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