不熟悉Timer的可以先看看MSDN的描述:計時器 伺服器計時器、Windows 計時器和線程計時器。
簡單來講:System.Windows.Forms.Timer基於Windows訊息迴圈,用事件方式觸發,在介面線程執行;System.Timers.Timer更精確,用事件方式觸發,線上程池執行;System.Threading.Timer設計為非常輕量級,用回呼函數引發,線上程池執行。概念約定:在一次執行未結束時,到了第二次執行的時間,如果第二次不等第一次結束便馬上執行,則稱為
重入。由於使用多線程,System.Timers.Timer和System.Threading.Timer是會重入的。那麼可以想象:1.計算時間的線程應該不是執行使用者函數的線程;2.執行線程不會總是同一個線程。下面重點討論System.Windows.Forms.Timer(下面簡稱WinTimer)。由於WinTimer是基於Windows訊息迴圈,顯然是為WinForm程式準備,所以WinTimer的引發都在介面線程。那麼理論上講,如果介面線程阻塞,則不可能收到WinTimer的事件。
看代碼:
public class Form1 : Form
{
Timer timer = new Timer();
int num = 0;
public Form1()
{
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
num++;
switch (num)
{
case 1: // 第一次
System.Threading.Thread.Sleep(3000);
Console.Write(num);
break;
case 2: // 第二次
timer.Stop();
Console.Write(num);
break;
}
}
}
是的,輸出視窗(“調試”->“視窗”->“輸出”)顯示為:12那麼,下面的代碼,你認為會怎麼輸出?
public class Form1 : Form
{
Timer timer = new Timer();
int num = 0;
public Form1()
{
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
num++;
int temp = num;
switch (num)
{
case 1: // 第一次
MessageBox.Show(num.ToString());
Console.Write(temp);
break;
case 2: // 第二次
timer.Stop();
Console.Write(temp);
break;
}
}
}
輸出:12?不見得!去運行它,你會發現,當你看到快顯視窗還沒來得及點擊的時候,輸出視窗就已經輸出2當你點擊快顯視窗的“確定”按鈕,輸出視窗會輸出1。也就是說,順序是:21!於是我們驚訝的發現:Thread.Sleep可以阻止WinTimer的事件再次觸發,而MessageBox.Show卻無法阻止!萬事皆有其因!對程式來講尤其如此。
分析:之所以Thread.Sleep阻止了WinTimer,是因為它阻塞了介面線程。而MessageBox.Show呢?它也阻塞了介面啊!從程式運行過程分析,MessageBox.Show必須要等我們點擊了“確定”按鈕才會執行下一句代碼,通過逐句跟蹤我們發現程式確實停在了這裡。而case 2裡的Console.Write為什麼在這中間還能得到運行呢?將case 2裡的Console.Write行代碼設定斷點。運行。斷點被命中的時候,開啟呼叫堆疊視窗(“調試”->“視窗”->“呼叫堆疊”),你會看到兩個timer_Tick方法,雙擊下面那個timer_Tick,你會看到:
原來第二次timer事件在Message.Show方法裡!(有興趣的可以開啟“選項”->“調試”->勾掉“啟用'Just My Code'”,看內部方法)真相大白:Message.Show雖然阻塞了外面的代碼,但它裡面仍然有訊息迴圈,於是Timer的事件得以觸發。結論:當沒有訊息迴圈或被阻塞的時候,WinTimer的事件是觸發不了的,但此線程只要有訊息迴圈,WinTimer就可以正常觸發。Thread.Sleep以及任何無訊息迴圈的代碼,都可以阻塞訊息迴圈,也便阻塞了WinTimer;而Message.Show,Form.ShowDialog等,它們雖然阻塞了外面的訊息迴圈,但它們裡面也有訊息迴圈,所以不會阻塞WinTimer。
附:既然System.Windows.Forms.Timer是基於訊息迴圈,那麼當然只要有訊息迴圈就可以使用System.Windows.Forms.Timer,那麼WebForm添加System.Windows.Forms引用,也是可以這樣使用System.Windows.Forms.Timer的:
Code
public partial class _Default : System.Web.UI.Page
{
static System.Windows.Forms.Timer timer;
static int X = 0;
static _Default()
{
Thread thread = new Thread(delegate()
{
System.Windows.Forms.Form form = new System.Windows.Forms.Form();
form.Load += delegate
{
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
timer.Tick += delegate
{
X++;
};
timer.Start();
};
System.Windows.Forms.Application.Run(form);
});
thread.Start();
}
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = X.ToString();
}
我可沒推薦你真的這麼用!