[C#.NET][Thread] 執行緒定時器

  • 105367
  • 0
  • 2013-08-16

[C#.NET][Thread] 執行緒定時器

大內世界裡有三種定時器,所謂定時器的意思就是間隔時間呼叫方法,這功能大家並不陌生的。

其中 System.Timers.TimerSystem.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);
}


執行後會出現跨執行緒的錯誤,這時我們有兩個方式可以解決

image

 

Ssynchronizingobject 屬性:

很簡單,只要將要更新的Form類別丟進去:

 

this._TimersTimer.SynchronizingObject =this;

 

就可以解決跨執行緒的問題

image

 

委派方法:

先加入以下程式碼

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 });

 

再執行一次觀察

image

 

眼尖的捧油一定發現這兩種跨執行緒的執行結果不一樣,用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參數

image

image

 

image

image

 

duTime:定時器啟用後多久時間進行第一次呼叫。

Peried:間隔多久時間進行下一次呼叫。

 

建構函數中有一個參數叫TimerCallback,它的委派簽章長這樣,所以void CallbackMethod(object State) 的簽章格式(參數傳遞)要定義相同,object State也可以用來跟主程序溝通。

image

 

執行結果如下,可以發現OS只有派兩隻執行緒Thread[6]跟Thread[11]在處理這些工作。

image

ThreadTimer.zip

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo