ShowTimerEventFiredDelegate允許ShowTimerEventFired方法在UI線程上調用它自己Figure 顯示了發生這一切的代碼
通過查詢InvokeRequired屬性可以非常容易的知道你是否從當前線程可以安全的訪問Windows窗體控件在這個例子中如果列表框的InvokeRequired屬性為真窗體的BeginInvoke方法就可以被ShowTimerEventFired方法調用然後再被ShowTimerEventFiredDelegate方法調用這能夠保證列表框的Add方法在UI線程上執行
正如你所看到的當你編寫異步定時器事件時有許多問題需要意識到在使用SystemTimersTimer和SystemThreadingTimer之前我推薦你閱讀Ian Griffith的文章Windows Forms:Give Your NETbased Application a Fast and Responsive UI with Multiple Threads 該文刊登在MSDN雜志的年月份的期刊上
處理定時器事件重入
當和異步定時器事件打交道時如由SystemTimersTimer和SystemThreadingTimer產生的定時器事件有另外一個細微之處你需要考慮問題就是必須處理代碼重入如果你的定時器事件處理函數代碼執行時間比你的定時器引發定時器事件的時間間隔要長你預先又沒有采取必要的措施保護防止多線程訪問你的對象和變量你就會陷入調試的困境看一下下面的代碼片斷
private int tickCounter = ;
private void tmrTimersTimer_Elapsed(object sender SystemTimersElapsedEventArgse)
{
SystemThreadingInterlockedIncrement(ref tickCounter);
ThreadSleep();
MessageBoxShow(tickCounterToString());
}
假設你的定時器間隔屬性設置為毫秒你也許會奇怪當第一個信息框彈出時顯示的值是這是因為在這秒期間第一個定時器事件正在睡眠而定時器卻在不同的工作者線程上繼續產生時間消失事件因此在第一個定時器事件處理完成之前tickCounter變量被增加了次注意我使用了InterlockedIncrement方法以線程安全的方式增加tickCounter變量的值也有其它方法可以這樣做但是InterlockIncrement是為這種操作而特別設計的
解決這種問題的簡單方法就是在你的事件處理函數代碼塊中暫時禁止定時器接著再允許定時器就像下面的代碼
private void tmrTimersTimer_Elapsed(object sender SystemTimersElapsedEventArgse)
{
tmrTimersEnabled = false;
SystemThreadingInterlockedIncrement(ref tickCounter);
ThreadSleep();
MessageBoxShow(tickCounterToString());
tmrTimersTimerEnabled = true;
}
有了這段代碼消息框就會每秒鐘顯示一次就像你所期望的那樣tickCounter的值每次只增加另外一些可選的原始同步對象就是Monitor或mutex去確保所有將來的事件被排隊直到當前的事件處理函數執行完成
結論
為了快速方便的看到NET框架中這三個定時器類的不同之處見Figure 對三個類的比較當使用定時器類時有一點你要考慮的就是是否以使用Windows調度器去定期的運行標准的可執行程序來更簡單的解決問題
[] [] [] []
From:http://tw.wingwit.com/Article/program/net/201311/15303.html