2013-08-08 30 views
16

Khi bạn sử dụng Timer hoặc Thread sẽ chỉ chạy trong toàn bộ thời gian của chương trình, bạn có cần giữ tham chiếu đến chúng để ngăn chúng không bị thu gom rác không?Bộ hẹn giờ có thể tự động thu gom rác không?

Hãy để dành một thực tế là chương trình bên dưới có thể có timer làm biến tĩnh trong lớp, đây chỉ là ví dụ về đồ chơi để hiển thị sự cố.

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

     Console.ReadLine(); 
    } 

    private static void CreateTimer() 
    { 
     var program = new Program(); 

     var timer = new Timer(); 
     timer.Elapsed += program.TimerElapsed; 
     timer.Interval = 30000; 
     timer.AutoReset = false; 
     timer.Enabled = true; 
    } 

    private void TimerElapsed(object sender, ElapsedEventArgs e) 
    { 
     var timerCast = (Timer)sender; 

     Console.WriteLine("Timer fired at in thread {0}", GetCurrentThreadId()); 

     timerCast.Enabled = true; 
    } 

    ~Program() 
    { 
     Console.WriteLine("Program Finalized"); 
    } 

    [DllImport("kernel32.dll")] 
    static extern uint GetCurrentThreadId(); 
} 

Bộ hẹn giờ có được thu thập trong ví dụ trên không? Tôi đã chạy nó một thời gian và tôi không bao giờ có ngoại lệ cũng như một thông báo nói rằng ~Program() được gọi.

UPDATE: tôi phát hiện ra từ this question (thanks sethcran) mà chủ đề đang theo dõi bởi CLR, nhưng tôi vẫn muốn một câu trả lời về Timers.

+1

Loại 'Hẹn giờ' nào bạn đang sử dụng ở đây? WinForms, Threading, vv ... – JaredPar

+0

@JaredPar Bất kỳ trong số chúng (nhưng tôi đã sử dụng 'System.Timers' trong ví dụ trên). Tôi cũng tò mò nếu tôi cần phải giữ một tham chiếu đến bất kỳ chủ đề tôi tạo bằng cách sử dụng 'Thread' –

+0

http: // stackoverflow.com/questions/9011831/new-thread-and-garbage-collection – Sethcran

Trả lời

36

Đây chỉ là vấn đề với lớp System.Threading.Timer nếu bạn không lưu trữ một tham chiếu đến nó ở đâu đó. Nó có một số quá tải của hàm tạo, các đối tượng mang đối tượng trạng thái là quan trọng. CLR chú ý đến đối tượng trạng thái đó. Miễn là nó được tham chiếu ở đâu đó, CLR giữ bộ đếm thời gian trong hàng đợi hẹn giờ của nó và đối tượng bộ đếm thời gian sẽ không nhận được rác được thu thập. Hầu hết các lập trình viên sẽ không sử dụng đối tượng trạng thái đó, bài viết MSDN chắc chắn không giải thích vai trò của nó.

System.Timers.Timer là trình bao bọc cho lớp System.Threading.Timer, giúp dễ sử dụng hơn. Đặc biệt, nó sẽ sử dụng đối tượng trạng thái đó và giữ một tham chiếu đến nó miễn là bộ hẹn giờ được kích hoạt.

Lưu ý rằng trong trường hợp của bạn, thuộc tính Kích hoạt của bộ hẹn giờ là sai khi nó nhập trình xử lý sự kiện đã qua của bạn vì bạn có AutoReset = false. Vì vậy, bộ đếm thời gian đủ điều kiện để thu thập ngay sau khi trình xử lý sự kiện của bạn. Tuy nhiên, bạn sẽ không gặp khó khăn khi tham khảo đối số người gửi, bắt buộc phải đặt Đã bật trở lại thành true. Mà làm cho jitter báo cáo các tài liệu tham khảo để bạn không có một vấn đề.

Hãy cẩn thận với trình xử lý sự kiện đã qua. Bất kỳ ngoại lệ nào được ném vào bên trong phương pháp đó sẽ bị nuốt mà không cần chẩn đoán. Điều này cũng có nghĩa là bạn sẽ không đặt Enabled trở lại thành true. Bạn phải sử dụng try/catch để làm điều gì đó hợp lý. Nếu bạn không cố ý kết thúc chương trình của bạn, tối thiểu bạn sẽ cần phải cho chương trình chính của bạn biết rằng một cái gì đó không hoạt động nữa. Đưa Enabled = true trong mệnh đề cuối cùng có thể tránh bị thu thập bộ đếm thời gian thu thập, nhưng có nguy cơ bị chương trình của bạn ném đi các ngoại lệ lặp đi lặp lại.

+1

+1. Đặc biệt thích bạn đề cập đến việc loại trừ ngoại lệ điên rồ, đó là lý do chính tôi sẽ không sử dụng 'System.Timers.Timer'. –

+1

Câu trả lời chất lượng ... Tuy nhiên, _again_. – MoonKnight

+0

Nếu tôi sử dụng một ví dụ Threading.Timer không có trạng thái null cho ** callback gọi ** đơn lẻ (khoảng thời gian là 'Timeout.InfiniteTimeSpan') thì GC có thể thu thập bộ đếm thời gian sau khi gọi mà không gặp bất kỳ sự cố nào không? – Mergasov

3

Thêm mã này vào chương trình và chạy chương trình. Bạn sẽ thấy rằng bộ đếm thời gian KHÔNG được thu thập.

private void DoStuff() 
    { 
     CreateTimer(); 
     Console.WriteLine("Timer started"); 
     int count = 0; 
     for (int x = 0; x < 1000000; ++x) 
     { 
      string s = new string("just trying to exercise the garbage collector".Reverse().ToArray()); 
      count += s.Length; 
     } 
     Console.WriteLine(count); 
     Console.Write("Press Enter when done:"); 
     Console.ReadLine(); 
    } 

    private void Ticktock(object s, System.Timers.ElapsedEventArgs e) 
    { 
     Console.WriteLine("Ticktock"); 
    } 

    private void CreateTimer() 
    { 
     System.Timers.Timer t = new System.Timers.Timer(); // Timer(Ticktock, null, 1000, 1000); 
     t.Elapsed += Ticktock; 
     t.Interval = 1000; 
     t.AutoReset = true; 
     t.Enabled = true; 
    } 

Vì vậy, câu trả lời cho câu hỏi của bạn dường như là bộ hẹn giờ không đủ điều kiện để thu thập và sẽ không được thu thập nếu bạn không duy trì tham chiếu đến nó.

Thật thú vị khi lưu ý rằng nếu bạn chạy cùng một thử nghiệm với System.Threading.Timer, bạn sẽ thấy rằng bộ hẹn giờ được thu thập.

+0

Bạn đã nhận được loại bộ hẹn giờ sai. –

+0

@HansPassant: Cảm ơn. Tôi chạy lại thử nghiệm với 'System.Timers.Timer' và thấy rằng nó không được thu thập. Hấp dẫn. –