[C#.NET][Thread] 執行緒定時器
大內世界裡有三種定時器,所謂定時器的意思就是間隔時間呼叫方法,這功能大家並不陌生的。
其中 System.Timers.Timer與System.Threading.Timer 都是屬於ThreadPool執行緒的一種,System.Windows.Forms.Timer比較不符合這次的論點,暫時先忽略。
下方是使用的比較表
System.Timers.Timer | System.Threading.Timer | |
呼叫方法 | Elapsed 事件觸發 | TimerCallback回呼 |
間隔呼叫 | Interval 屬性 |
1.建構函數中的period參數 2.Change方法中的period參數 |
跨執行緒更新UI |
1.Ssynchronizingobject 屬性 2.委派方法 |
委派方法 |
接下來觀察這些定時器的狀態,首先加入命名空間
//匯入命名空間
using ThreadingTimer = System.Threading.Timer;
using TimersTimer = System.Timers.Timer;
//程式開始
ThreadingTimer _ThreadTimer = null;
TimersTimer _TimersTimer = null;
private void Form1_Load(object sender, EventArgs e)
{
Thread t = Thread.CurrentThread;
bool IsThreadPool = t.IsThreadPoolThread;
bool IsBackground = t.IsBackground;
t.Name = "Main Thread";
string msg = string.Format("Thread[{0}]:{1},Is ThreadPool=[{2}],Is Background=[{3}]", t.ManagedThreadId, t.ThreadState, IsThreadPool, IsBackground);
this.Text = msg;
}
System.Timers.Timer使用方式
- 實體化
- 用Interval 定義間隔呼叫週期時間
- Elapsed 事件呼叫要執行的方法(要重覆執行的方法)
- Start 方法啟用定時器,開始觸發 Elapsed
- Stop 方法停止定時器,停止觸發 Elapsed
private void button2_Click(objectz sender, EventArgs e)
{
this._TimersTimer = new TimersTimer();
this._TimersTimer.Interval = 100;
this._TimersTimer.Elapsed += new System.Timers.ElapsedEventHandler(_TimersTimer_Elapsed);
this._TimersTimer.Start();
}
void _TimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Thread t = Thread.CurrentThread;
bool IsThreadPool = t.IsThreadPoolThread;
bool IsBackground = t.IsBackground;
string msg = string.Format("Thread[{0}]:{1},Is ThreadPool=[{2}],Is Background=[{3}]", t.ManagedThreadId, t.ThreadState, IsThreadPool, IsBackground);
this.listBox2.Items.Add(msg);
}
執行後會出現跨執行緒的錯誤,這時我們有兩個方式可以解決
Ssynchronizingobject 屬性:
很簡單,只要將要更新的Form類別丟進去:
this._TimersTimer.SynchronizingObject =this;
就可以解決跨執行緒的問題
委派方法:
先加入以下程式碼
delegate void UpdateControl(Control Ctrl, string Msg);
private object _objLock = new object();
void _mUpdateControl(Control Ctrl, string Msg)
{
lock (this._objLock)
{
if (Ctrl is ListBox)
((ListBox)Ctrl).Items.Add(Msg);
}
}
接著把 _TimersTimer_Elapsed方法裡的
this.listBox2.Items.Add(msg);
改成
this.BeginInvoke(new UpdateControl(_mUpdateControl), new object[] { this.listBox2, msg });
再執行一次觀察
眼尖的捧油一定發現這兩種跨執行緒的執行結果不一樣,用Ssynchronizingobject 屬性所得到的Thread屬性是非背景執行緒,而且執行緒ID跟主執行緒相同,因為Ssynchronizingobject 是接收我們傳入的Windows Form類別,由它來幫我們處理非同步更新UI,所以執行緒ID跟主執行緒一樣;反之使用委派方法更新UI,還是一樣保持背景執行緒的狀態。
System.Threading.Timer使用方式
- 實體化呼叫建構函數,傳入TimerCallback委派方法(要重覆執行的方法),傳入間隔時間。
- 使用Change方法變更週期時間。
- 使用Dispose停止定時器。
private void button1_Click(object sender, EventArgs e)
{
string currentName = new StackTrace(true).GetFrame(0).GetMethod().Name;
this._ThreadTimer = new ThreadingTimer(new System.Threading.TimerCallback(CallbackMethod), currentName, 1, 100);
}
void CallbackMethod(object State)
{
string methodName = State.ToString();
Thread t = Thread.CurrentThread;
bool IsThreadPool = t.IsThreadPoolThread;
bool IsBackground = t.IsBackground;
string msg = string.Format("Thread[{0}]:{1},Is ThreadPool=[{2}],Is Background=[{3}]", t.ManagedThreadId, t.ThreadState, IsThreadPool, IsBackground);
this.BeginInvoke(new UpdateControl(_mUpdateControl), new object[] { this.listBox1, msg });
}
我們僅需要一個Callback方法就夠了,當然如果你還需要更新UI的話,還需要委派方法來跨執行緒更新UI;至於如何定義定時器觸發週期,就看建構函數還有Change方法裡面的dueTime和period參數
duTime:定時器啟用後多久時間進行第一次呼叫。
Peried:間隔多久時間進行下一次呼叫。
建構函數中有一個參數叫TimerCallback,它的委派簽章長這樣,所以void CallbackMethod(object State) 的簽章格式(參數傳遞)要定義相同,object State也可以用來跟主程序溝通。
執行結果如下,可以發現OS只有派兩隻執行緒Thread[6]跟Thread[11]在處理這些工作。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET