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






