作者 | 朱鋼
責編 | 郭芮
Timer 計時器是在 C# 開發中經常用到的,但是有很多開發人員對它并不了解,今天這篇文就具體講解一下 C# 中的計時器。
在 C# 中存在3種常用的 Timer :
-
System.windows.Forms.Timer
-
System.Timers.Timer
-
System.Threading.Timer
System.Windows.Forms.Timer
這個 Timer 是單線程的,也就是說只要它運行,其他線程就要等著。
這個 Timer 有如下特點:
-
完全基于 UI 線程,定時器觸發時,操作系統把定時器消息插入線程消息隊列中,調用線程執行一個消息泵提取消息,然后發送到回調方法 Tick 中;
-
使用 Start 和 Stop 啟動和停止 Timer;
-
UI 操作過長會導致 Tick 丟失;
-
可以使用委托 Hook Tick 事件;
-
精確度不高;
-
通過將 Enabled 設置為 True,使 Timer 自動運行。
從上面的第一個特點可得知,該 Timer 會造成 WinForm UI 假死,因此如果需要定時處理大量計算或者大量 IO 操作的任務,不建議使用該 Timer。接下來我們看一個例子體會一下在IO操作的情況下出現的假死情況。
我們在 Form 中放入兩個 Button、一個 Lable 和一個 Timer:
private void Button_Click(object sender, EventArgs e)
{
timer.Interval = 1000;
timer.Tick += Timer_Tick;
timer.Start;
}
private void Timer_Tick(object sender, EventArgs e)
{
for (int i = 0; i < 10000; i++)
{
File.AppendAllText(Directory.GetCurrentDirectory+"test.txt", i.ToString);
this.label_output.Text = "當前操作:插入數字" + i;
}
}
我們單擊計算按鈕,我們會發現 WinForm 出現了假死(無法移動窗口、按鈕無法點擊等)。
System.Timers.Timer
該 Timer 基于服務器,是為在多線程環境中用于輔助線程而設計的,可以在線程間移動來處理引發的 Elapsed 事件,比上一個計時器更加精確。
該 Timer 有如下特點:
-
通過 Elapsed 設置回掉處理事件,且 Elapsed 是運行在 ThreadPool 上的;
-
通過 Interval 設置間隔時間;
-
當 AutoReset 設置為 False 時,只在到達第一次時間間隔后觸發 Elapsed 事件;
-
是一個多線程計時器;
-
無法直接調用 WinForm 上的控件,需要使用委托;
-
主要用在 Windows 服務中。
同樣我們通過代碼來看一下該 Timer 計時器怎么使用:
System.Timers.Timer timersTimer = new System.Timers.Timer;
private void Button_Click(object sender, EventArgs e)
{
timersTimer.Interval = 1000;
timersTimer.Enabled = true;
timersTimer.Elapsed += TimersTimer_Elapsed;
timersTimer.Start;
}
private void TimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
this.BeginInvoke(new Action( =>
{
this.label_output.Text="當前時間:"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}), );
}
}
private void Button1_Click(object sender, EventArgs e)
{
timersTimer.Stop;
}
運行上面代碼,會發現 WinForm 界面假死的情況消失了。
System.Threading.Timer
該 Timer 同樣也是一個多線程的計時器,它有如下特點:
-
多線程;
-
和前兩個計時器相比沒有 Start 和 Stop 方法,如果要停止計時器,必須調用 Dispose 方法來銷毀 Timer 對象;
-
調用 Dispose 方法后并不能馬上停止所有的計時器,這是因為間隔時間小于執行時間時多個線程運行造成的,多個線程無法同時停止;
-
是一個輕量級的計時器;
-
所有的參數全部在構造函數中進行了設置;
-
可以設置啟動時間;
-
不建議在 WinForm 程序中使用。
我們來看一下代碼(在控制臺應用程序中輸入以下代碼):
static System.Threading.Timer threadingTimer;
static int numSum = 0;
static void Main(string[] args)
{
threadingTimer = new System.Threading.Timer(new System.Threading.TimerCallback(threadingTimer_Elapsed), , 0, 1000);
Console.Read;
}
private static void threadingTimer_Elapsed(object state)
{
for (int i = 0; i < 10000; i++)
{
numSum++;
Console.WriteLine("輸出數字:"+i);
}
if (numSum > 10000)
{
threadingTimer.Dispose;
Console.WriteLine("結束");
}
}
注意,當我們不再需要多線程 Timer 計時器的時候,我們可以調用 Dispose 方法釋放所占有的資源。但是因為 Timer 計時器是按線程池線程來安排回調執行的,因此回調可能發生在 Dispose 方法的重載被調用之后,所以我們可以使用可使用 Dispose(WaitHandle) 方法等待所有回掉完成。
總結
綜上所屬我們總結出 C# 中不同 Timer 計時器的特點和使用環境:
作者:朱鋼,.NET高級開發工程師,7年一線開發經驗,參與過電子政務系統和AI客服系統的開發,以及互聯網招聘網站的架構設計,目前就職于北京恒創融慧科技發展有限公司。
本文系作者投稿,版權歸其所有,首發于CSDN博客,原文鏈接:
https://blog.csdn.net/gangzhucoll/article/details/93744022