C#Invoke委託在多執行緒中的使用

無聲蟬發表於2018-10-19

一步一步跟隨著我的腳步看一看

Form上我設定一個button,text屬性為“點選開始測試”;現在我想點選它以後,動態變化從1~10賦值

(1)

第一個映入腦中的方式就是這樣寫

private void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    for(int i=1;i<10;i++)
    {
        button1.Text = i.ToString();
        Thread.Sleep(1000);
    }
    button1.Text = "點選開始測試";
    button1.Enabled = true;
}

有沒有問題?肯定有的啦,我點選以後,介面倒是disable了,但是沒有1~10出現,時間一到,恢復enable。為什麼呢?

原因:直接主執行緒休眠是達不到效果的,此時桌面還處於假死狀態,更新不了text值。程式碼放在了UI執行緒執行,阻塞了UI的顯示,所以中間的結果你看不到。實驗了兩種方式解決:

  1. 在Thread.Sleep(1000);後加 button1.Refresh();
  2. 在Thread.Sleep(1000);後加 Application.DoEvents();

(2)

既然是由於程式碼放在了UI執行緒,那我新建個執行緒,在那裡面更新UI控制元件好了。

private void button1_Click(object sender, EventArgs e)
{
    //啟動一個執行緒
    new Thread(ThreadTask).Start();
}
//執行緒函式
public void ThreadTask()
{
    button1.Enabled = false;
    for (int i = 0; i < 10; i++)
    {
        button1.Text = i.ToString();
        Thread.Sleep(1000);
    }
    button1.Text = "點選開始測試";
    button1.Enabled = true;
}

出錯了!!!

造成這種異常的原因在於,控制元件是在主執行緒中建立的(比如this.Controls.Add(...);),進入控制元件的事件響應函式時,是在控制元件所在的執行緒,並不是主執行緒。在控制元件的事件響應函式中改變控制元件的狀態,可能與主執行緒發生執行緒衝突。如果主執行緒正在重繪控制元件外觀,此時在別的執行緒改變控制元件外觀,就會造成畫面混亂。

(3)

下面該主角出場了,C#的委託機制,由於歷史的原因,有這麼幾種形式:

//第一種
button1.Invoke(new EventHandler(delegate{button1.Text = "關閉";}));
//第二種
this.Invoke(new EventHandler(delegate{button1.Text = "關閉";}));
//第三種 網上說自C# 3.0開始就有了
this.Invoke(new Action(() =>{ button1.Text = "關閉";}));

我喜歡用第三種,好記,哈哈。好了,接下來實現以下吧!

private void button1_Click(object sender, EventArgs e)
{         
    //啟動一個執行緒
    new Thread(ThreadTask).Start();
}
        //執行緒函式
public void ThreadTask()
{
    //首先將button物件禁用
    this.Invoke(new Action(() =>
    {
        button1.Enabled = false;
    }));
    for (int i = 0; i < 10; i++)
    {
        this.Invoke(new Action(() =>
        {
            button1.Text = i.ToString();
        }));
        Thread.Sleep(1000);
    }
    //雖然不是迴圈內,請不要忘記,你的呼叫依然在輔助執行緒中,所以,還是需要invoke的。
    //還原狀態,設定按鈕的文字為初始狀態,設定按鈕可用。
    this.Invoke(new Action(() =>
    {
        button1.Text = "點選開始測試";
        button1.Enabled = true;
    }));

    

}

大功告成嘍!!

 

相關文章