2012-05-18 36 views
7

Tôi có mã kiểm tra như thế này:finalizer không được gọi sau khi ngoại lệ unhandled ngay cả với CriticalFinalizerObject

public class A : CriticalFinalizerObject 
{ 
    ~A() 
    { 
     File.WriteAllText("c:\\1.txt", "1z1z1"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(); 
     throw new Exception(); 
    } 
} 

Trước tiên tôi đã cố gắng chạy nó mà không phát sinh A từ CriticalFinalizerObject. Finalizer không được gọi sau khi kết thúc chương trình này. Điều đó làm tôi ngạc nhiên khi tôi nghĩ nó mang tính quyết định hơn nhưng không sao. Sau đó, tôi đã đọc về CriticalFinalizerObject's đảm bảo finalizers của họ sẽ được gọi. Tôi bắt nguồn A từ nó. Đoán xem. Nó vẫn không bị xử tử. Tôi đang làm gì/hiểu sai?

(Vui lòng không viết nội dung rõ ràng về người thu gom rác là không xác định, tôi biết điều đó. Không phải là trường hợp chương trình kết thúc và tôi tưởng tượng mình có thể dọn dẹp an toàn sau khi được miễn trừ quản lý ngoại lệ .)

Trả lời

9

Trước hết, chúng ta hãy đọc về CriticalFinalizerObject trong MSDN, chúng ta có thể đọc, rằng:

Trong các lớp học có nguồn gốc từ các nhà phê bình Lớp alFinalizerObject, thời gian chạy ngôn ngữ chung (CLR) đảm bảo rằng tất cả các mã quyết toán quan trọng sẽ có cơ hội thực thi, miễn là các finalizer tuân theo các quy tắc cho CER, ngay cả trong trường hợp CLR buộc unloads miền ứng dụng hoặc hủy bỏ chủ đề.

Từ chính ở đây là UNLOAD.

Thứ hai, chúng ta hãy đọc MSDN một lần nữa, lần này là về ngoại lệ trong chủ đề quản lý:

Nếu những trường hợp ngoại lệ là unhandled trong thread chính, hoặc trong chủ đề đó vào thời gian chạy từ mã không được quản lý, họ tiến hành bình thường, dẫn đến việc chấm dứt chấm dứt của đơn đăng ký.

Từ chính là CHẤM DỨT.

Vì vậy, khi có một ngoại lệ chưa được giải quyết trong chủ đề chính - ứng dụng chấm dứt, nhưng CriticalFinalizerObject chỉ giúp khi dỡ miền.

Ví dụ, CriticalFinalizerObject lon giúp trong tình huống như vậy:

// Create an Application Domain: 
AppDomain newDomain = AppDomain.CreateDomain("NewApplicationDomain"); 

// Load and execute an assembly: 
newDomain.ExecuteAssembly(@"YouNetApp.exe"); 

//Unload of loaded domain 
AppDomain.Unload(newDomain); 

Đây là một tình huống, nơi miền đã được bốc dỡ, và CriticalFinalizerObject đảm bảo với bạn, rằng finalizer của bạn sẽ được gọi.

Trong tình huống của bạn với chấm dứt các ứng dụng bạn có thể thử để đăng ký

AppDomain.CurrentDomain.UnhandledException 

và tự hoàn thiện đối tượng của bạn.

UPD: Jeffrey Richter trong cuốn sách của ông "CLR thông qua C#" viết về CriticalFinalizerObject, rằng đó là vì tình huống mà bạn gửi mã của bạn ví dụ như SQLServer, có thể chạy C# như một thủ tục. Trong trường hợp này CriticalFinalizerObject giúp bạn làm sạch đối tượng của bạn, nếu SQLServer sẽ dỡ bỏ tên miền của thư viện của bạn. CriticalFinalizerObject cũng cho các tình huống mà bạn cần trong finalizer của đối tượng để gọi method của đối tượng khác, vì CriticalFinalizerObject đảm bảo với bạn, rằng finalizer sẽ được gọi sau finalizers của tất cả các đối tượng CriticalFinalizerObject không.

+0

Cảm ơn câu trả lời chi tiết! Tôi sẽ nói những từ như "chấm dứt" và "không tải" không được xác định rất rõ trong các tài liệu MS này. Theo nghĩa thông thường, có một cái gì đó "chấm dứt" không nhất thiết có nghĩa là nó sẽ không được "dỡ", mặc dù chúng ta thấy nó không giống như ở đây ... – IlyaP

+0

Tôi đã thêm một số cập nhật để trả lời từ "CLR via C#" – igofed

0

Ok. Đã viết một bài kiểm tra đơn giản. Nếu tôi có một ngoại lệ trong hàm tạo, nơi tôi tạo đối tượng A, thực sự không thể làm cho finalizer làm việc, nhưng khi tôi tạo một đối tượng không phải trong hàm tạo của lớp khác mà trong phương thức khác, và sau đó ném ngoại lệ nó hoạt động. Vì vậy, tôi nghi ngờ, nếu constructor không bao giờ hoàn thành việc xây dựng class, và tạo của nó chấm dứt, đối tượng không bao giờ được tạo, stack xóa, các đối tượng được loại bỏ khỏi heap mà không hoàn thành như chúng không bao giờ xảy ra.

Đó là phỏng đoán của tôi. Nhưng để giải quyết vấn đề này, tôi sẽ bao bọc các đối tượng xây dựng mã quan trọng trong try-catch-finally và gọi mã clean up một cách rõ ràng.

Ví dụ: này đã làm việc

public Form1() 
     { 
      InitializeComponent(); 
     } 
     protected override void OnLoad(EventArgs e) 
     { 
      base.OnLoad(e); 
      var a = new A(); 
      throw new Exception(); 
     } 
    } 

này không

public Form1() 
     { 
      InitializeComponent(); 
      var a = new A(); 
      throw new Exception(); 
     } 

    } 
+0

Nó không được gọi - như bạn thấy, tôi tạo một tệp trong trình hoàn thiện - nó không bao giờ xuất hiện. – IlyaP

+0

GC.Collect() ở đâu? Nếu trước khi ném ngoại lệ thì chắc chắn nó sẽ hoạt động. Nhưng tôi không thể viết bất kỳ mã nào sau khi ném ... – IlyaP

+1

Để xử lý không được giải quyết, bạn vẫn có thể sử dụng sự kiện 'AppDomain.CurDomain.UnhandledException'. –

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