2012-03-30 24 views
7

Cấu trúc bị hủy là lạ. Tôi đã cố gắng loại bỏ nhu cầu sử dụng mẫu dùng một lần bằng cách sử dụng quản lý tham chiếu 'thông minh', đảm bảo rằng bộ thu gom rác có thể thu thập các đối tượng vào đúng thời điểm. Trong một trong những kẻ hủy diệt của tôi, tôi đã phải đợi một sự kiện từ một vật thể khác, điều mà tôi nhận thấy nó không xảy ra. Ứng dụng này chỉ đơn giản là tắt và destructor đã được chấm dứt ở giữa thực hiện. Tôi mong đợi một destructor luôn được phép kết thúc chạy, nhưng như kiểm tra sau đây chỉ ra rằng đó là không đúng sự thật.Không destructors đảm bảo để kết thúc chạy?

using System; 
using System.Diagnostics; 
using System.Threading; 


namespace DestructorTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      new DestructorTest(); 
      new LoopDestructorTest(); 
      using (new DisposableTest()) { } 
     } 
    } 

    class DestructorTest 
    { 
     ~DestructorTest() 
     { 
      // This isn't allowed to finish. 
      Thread.Sleep(10000); 
     }  
    } 

    class LoopDestructorTest 
    { 
     ~LoopDestructorTest() 
     {   
      int cur = 0; 
      for (int i = 0; i < int.MaxValue; ++i) 
      { 
       cur = i; 
      } 
      // This isn't allowed to finish. 
      Debug.WriteLine(cur); 
     } 
    } 

    class DisposableTest : IDisposable 
    { 
     public void Dispose() 
     { 
      // This of course, is allowed to finish. 
      Thread.Sleep(10000); 
     } 
    } 
} 

Vì vậy, không phải là trình phá hủy được đảm bảo để kết thúc chạy?

+3

Đang chờ sự kiện từ một đối tượng khác trong trình kết thúc? Đây là điên rồ! – MattDavey

+0

Bạn thực sự nên gắn bó với mẫu 'IDisposable' để có sự hủy diệt đáng tin cậy và giải phóng tài nguyên. –

+0

@MattDavey ... và chờ một sự kiện từ một đối tượng khác tiếp tục kích hoạt 'Dispose()' không phải là điên rồ? Có gì khác biệt? –

Trả lời

15

Vì vậy, không phải là trình phá hủy được đảm bảo để kết thúc chạy?

Không. Từ những gì tôi nhớ, khi quá trình chấm dứt nó cung cấp cho finalizers một vài giây để thực thi, nhưng sau đó chấm dứt quá trình đột ngột. Bạn sẽ không muốn một finalizer xấu để ngăn chặn một quá trình từ bao giờ kết thúc, phải không?

Bạn nên coi việc hoàn thiện là một nỗ lực "nỗ lực tốt nhất" - đặc biệt là không sẽ xảy ra trong các tình huống mà toàn bộ hệ thống bị tắt đột ngột, chẳng hạn như BSOD hoặc mất điện.

EDIT: Tôi đã tìm thấy một số giả tài liệu dưới dạng một blog post from Joe Duffy:

Nếu một khóa mồ côi trong quá trình ngăn chặn tất cả các chủ đề hoạt động, sau đó, con đường đang tắt máy sẽ thất bại trong việc lấy khóa. Nếu những vụ mua lại này được thực hiện với thời gian chờ không (hoặc thời gian chờ lâu) sẽ bị thu hồi thì sẽ xảy ra lỗi treo. Để đối phó với điều này (và bất kỳ loại khác của hang có thể xảy ra), CLR chỉ định một thread watchdog để giữ một mắt trên thread finalizer. Mặc dù cấu hình, theo mặc định CLR sẽ cho phép finalizers chạy trong 2 giây trước khi trở nên thiếu kiên nhẫn; nếu hết thời gian chờ này, luồng kết thúc được dừng lại, và tắt máy tiếp tục mà không làm cạn kiệt phần còn lại của hàng đợi finalizer.

+1

Thực ra tôi muốn, hoặc ít nhất tôi muốn nhận được cảnh báo lớn trong Visual Studio ứng dụng không hoàn thành chính xác (như không phải tất cả các destructors đều được phép hoàn thành đang chạy). –

+6

@StevenJeuris: Có vẻ như bạn đang dựa vào finalizers trong một tình huống mà bạn thực sự * không nên *. Finalizers nên * giúp * để tránh rò rỉ tài nguyên - nếu hệ thống của bạn sẽ kết thúc ở trạng thái xấu nếu chúng không chạy đến khi hoàn thành, bạn nên thiết kế lại. Sau khi tất cả, luôn luôn có khả năng rằng họ * sẽ không * chạy do một BSOD, cúp điện vv. –

+0

Vâng việc thiết kế lại tất nhiên liên quan đến việc sử dụng 'IDisposable', mà không phải là chấm dứt đột ngột vì nó phải chờ đợi x ms cho một sự kiện cho biết bước xử lý đã hoàn thành và bước tiếp theo có thể được thực thi. Tôi chỉ không thấy điểm hủy diệt sau đó. Trong tình huống nào _should_ bạn dựa vào chúng? –

1

Vì vậy, không phá hủy được đảm bảo để kết thúc chạy?

Mặc dù mã không nằm trong mã của bạn nhưng có thể có trường hợp trong đó GC.SuppressFinalize được gọi rõ ràng từ phương pháp Dispose(). Điều này ngăn chặn việc hoàn thiện, và dành cho các đối tượng không yêu cầu nó.

Điều này có thể cải thiện hiệu suất đáng kể, vì đối tượng cuối cùng sẽ luôn tồn tại trong một bộ sưu tập rác, tức là nó sẽ được thăng cấp thành gen1 hoặc thậm chí gen2, có chi phí cao hơn.

1

.NET không đi kèm với trình phá hủy. Mã của bạn chứa finalizers thay thế.

Trình hoàn thành được gọi khi đối tượng là rác thu thập được, không phải khi đối tượng bị vô hiệu hóa. Họ cũng nhận được thời gian thực hiện hạn chế để ngăn chặn các đối tượng treo.

Xem thêm https://en.wikipedia.org/wiki/Finalizer.

+1

Nhưng C# chắc chắn không có [destructors] (http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx) –

+0

Những người viết thông số C# đã quyết định rằng phần tử cú pháp được chỉ định bằng dấu ngã, sau đó là một tên lớp được gọi chính thức là một destructor. Tôi nghĩ rằng cú pháp và thuật ngữ phá hủy đều là ngớ ngẩn, vì (1) thuật ngữ "destructor" đã được các ngôn ngữ khác sử dụng, có nghĩa là cái gì đó khác và không liên quan; (2) thuật ngữ "finalizer" tồn tại và mô tả những gì C# destructor thường cố gắng làm, mặc dù một destructor kết thúc tốt đẹp một finalizer trong một số mã khác; (3) sử dụng đúng một hàm hủy thường yêu cầu 'GC.SuppressFinalize()' và 'GC.KeepAlive()' .... – supercat

+0

... vì vậy trừ khi có các khung công tác hỗ trợ hai hàm sau nhưng thực hiện hoàn thiện thông qua một số phương thức khác hơn là ghi đè 'Object.Finalize', việc sử dụng một destructor không còn di động hơn là sẽ ghi đè' Object.Finalize', nếu C# cho phép tiến trình hành động sau. – supercat

0

Một cách khác để xem xét là các trình kết thúc được gọi khi bộ thu gom rác giải phóng bộ nhớ. Tuy nhiên nếu bạn có một chương trình đòi hỏi tối đa 1Mb bộ nhớ nhưng đã chạy trên một máy tính với 10Mb bộ nhớ thì việc triển khai hợp lệ bộ thu gom sẽ không làm gì cả (vì sẽ có đủ bộ nhớ cho chương trình). để thực hiện). Trong trường hợp đó không có finalizers sẽ được gọi.

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