C#三種定時器


三個定時器分別是

  1. 實現按用戶定義的時間間隔引發事件的計時器。此計時器最宜用於 Windows 窗體應用程序中,並且必須在窗口中使用。
    System.Windows.Forms.Timer
  2. 提供以指定的時間間隔執行方法的機制。無法繼承此類。
    System.Threading.Timer
  3. 在應用程序中生成定期事件。
    System.Timers.Timer

Forms.Timer

這個timer跟js中的定時器道理相同,它並不開辟一個線程,它只是把事件加入到隊列中,它用的是UI線程。所以它的好處就是輕便簡單。因為沒有實體不消耗資源,所以這個timer無法回收。窗體timer和線程timer、計時器timer不同,因為后兩者dispose之后,GC可以收集,而前者無法收集。
使用這個Forms.Timer不能執行IO,網絡請求等耗時操作,否則阻礙UI線程,界面卡住不動。

Timers.Timer和Threading.Timer都會開辟線程。

Timers.Timer

System.Timers.Timer只要處於活動狀態,就會一直存在下去,直到你手工停止或宿主線程結束。MSDN上還有這樣一段話“Elapsed 事件在 ThreadPool 線程上引發。如果 Elapsed 事件的處理時間比 Interval 長,在另一個 ThreadPool 線程上將會再次引發此事件。因此,事件處理程序應當是可重入的。”也就是說,在你在每次的Elapsed 事件處理在下一次輪循時間到來的時候還沒有結束,Timer對象仍然會另一個線程中啟動Elapsed 的處理事件。這種機制的后果就可能會導致你的Timer已經被結束了,但是還會再執行Elapsed事件,MSDN的原文:“在一個線程調用 Stop 方法或將Enabled 屬性設置為 false 的同時,可在另一個線程上運行事件處理方法。這可能導致在計時器停止之后引發 Elapsed 事件。”針對這種情況,如果你不願讓它發生,你可能就必須做一些額外的工作來避免它的發生。這種機制同樣也適用於System.Threading.Timer。

Threading.Timer

System.Threading.Timer : MarshalByRefObject, IDisposable 這是一種輕量經的計時數,它在使用上與System.Timers.Timer的不同表現在:使用回調機制,而不是事件機制。構造器中可以指定首次執行時間(構建后或修改后開始算)和間隔執行時間,這兩個時間(dueTime,period)可以是不同的。它是沒有開始和結束控制接口,從構建開始算,直至釋放結束。它基於ThreadPool線程機制,遵循着上述System.Timers.Timer的相同原則(紅色部分)。同時它在生命周期方面也有必須要注意的地方,它沒有開始或停止(有釋放接口Dispose)方法。在它的生命周期中,必須被其它對象所引用。一旦它不被任何對象所引用,那么就意味着這個Timer對象變成一個不可達對象,會被GC回收。MSDN原文解釋:
“只要在使用 Timer,就必須保留對它的引用。對於任何托管對象,如果沒有對 Timer 的引用,計時器會被垃圾回收。即使 Timer 仍處在活動狀態,也會被回收。”

如何選擇三種計時器

MSDN中還有這樣一段話:“System.Threading..::.Timer is a simple, lightweight timer that uses callback methods and is served by thread pool threads. It is not recommended for use with Windows Forms, because its callbacks do not occur on the user interface thread. System.Windows.Forms..::.Timer is a better choice for use with Windows Forms. For server-based timer functionality, you might consider using System.Timers..::.Timer, which raises events and has additional features.”它告訴我們:System.Threading.Timer是一個簡單的,輕量級的,利用回調機制和線程池機制的計時器。在Windows Forms的場景下不建議我們使用這個對象,因為UI線程並不觸發回調函數,取而代之是Windows.Forms.Timer ,而如果希望利用基於服務器計時器的功能,則建議我們使用System.Timers.Timer。System.Windows.Forms.Timer : System.ComponentModel.Component 這一種專門服務於Windows Forms的計時器,它在機制和原理上都與前面兩種有着比較大的區別。在接口使用上與System.Timers.Timer比較相似,同時具備了一些的Windows Form控件的特征。同時它的精度設計上也不是很高。“Timer 用於以用戶定義的事件間隔觸發事件。Windows 計時器是為單線程環境設計的,其中,UI 線程用於執行處理。它要求用戶代碼有一個可用的 UI 消息泵,而且總是在同一個線程中操作,或者將調用封送到另一個線程。使用此計時器時,請使用 Tick 事件執行輪詢操作,或在指定的時間內顯示啟動畫面。每當Enabled 屬性設置為 true 且 Interval 屬性大於 0 時,將引發 Tick 事件,引發的時間間隔基於Interval 屬性設置。”

非控件的創建線程不能操作控件。否則運行不報錯但調試會報錯。

關於垃圾回收

MSDN的描述: 只要在使用 Timer,就必須保留對它的引用。對於任何托管對象,如果沒有對 Timer 的引用,計時器會被垃圾回收。即使 Timer 仍處在活動狀態,也會被回收。當不再需要計時器時,請使用 Dispose 方法釋放計時器持有的資源。如果希望在計時器被釋放時接收到信號,請使用接受 WaitHandle 的 Dispose(WaitHandle) 方法重載。計時器已被釋放后,WaitHandle 便終止。
而我說:如果一個對象的成員函數正在被執行,那么這個對象肯定不會被收集
要想對無引用的對象進行收集,就必須要終止這個對象的一切timer成員,否則無法收集該對象(因為timer正在使用該對象)。如果不停止timer就直接更改指針(這個對象徹底無法訪問了),這就是新時代的內存泄露,程序早晚要內存溢出
timer.stop 和timer.dispose 都會使線程終止,從而可以回收垃圾

所以謹慎為好,不要過於依賴編譯器,這個問題是個問題,養成好的編程習慣才能避免很多大坑。

using System;
class TimersTimer {
    System.Timers.Timer t;
    public TimersTimer() {
        Console.WriteLine("Timers.Timer is created");
        t = new System.Timers.Timer();
        t.Interval = 1;
        t.Elapsed += tick;
        t.Start();
    }
    void tick(object o, EventArgs e) {
        t.Stop();
        Console.WriteLine("tick is called");
    }
    ~TimersTimer() {
        Console.WriteLine("Timers.Timer is died");
    }
}
class FormTimer {
    System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
    public FormTimer() {
        Console.WriteLine("Form.Timer is called");
        t.Interval = 1;
        t.Tick += tick;
        t.Start();
    }
    void tick(object o, EventArgs e) {
        Console.WriteLine("tick is called");
        t.Dispose();
    }
    ~FormTimer() {
        Console.WriteLine("Form.Timer is deleted");
    }
}
class ThreadTimer {
    System.Threading.Timer t;
    public ThreadTimer() {
        Console.WriteLine("thread.timer is called");
        t = new System.Threading.Timer(new System.Threading.TimerCallback(tick), null, 0, 1);
    }
    void tick(object o) {
        Console.WriteLine("tick is called");
        t.Dispose();
    }
    ~ThreadTimer() {
        Console.WriteLine("thread.timer is died");
    }
}
class haha {
    static void Main() {
        new ThreadTimer();
        System.Threading.Thread.Sleep(100);
        GC.Collect();
        System.Windows.Forms.Application.Run(); 
    }
} 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM