跨執行緒對控件做控制,使用BackgroundWorker

使用BackgroundWorker跨執行緒對控件做控制,不再讓迴圈當掉你的程式

從以前到現在,指導學弟妹寫畢業專題都會要求他們練習一些常見且重要的技術與技巧,其中一個題目"不用Timer使Label顯示時間"幾乎沒有人寫出來過,其實解題的方法隱藏在課本中,相信資訊相關科系的人都拜讀過"作業系統原理"這本大作(俗稱的:恐龍本),在課本中絕對都有提到執行緒的概念,只是很多人有讀沒有懂。

在.Net中,微軟給了大家非常好用的工具:BackgroundWorker、System.Threading,本篇的主題先以BackgroundWorker為主角,下次再介紹System.Threading。

你可以在工具列中找到BackgroundWorker的蹤跡,但並不是每一種專案類型都有,像是智慧型裝置相關的專案就沒有此工具可用,必須乖乖的使用System.Threading,我們以WindowsFormsApplication為主要的範例專案。

主題開始之前,先提供一個很多新手常發生的錯誤:


	private void Form1_Load(object sender, EventArgs e)
{
    for (; ; )
        textBox1.Text = DateTime.Now.ToString();
}

這個錯誤的範例保證可以Compile,也絕對可以Debug,執行檔也絕對可以執行,但是執行後一定馬上當掉,因為這個程式的執行緒都被一個無線迴圈給卡死了,沒有空閒去做別人的工作,理所當然程式就擋掉了。

接著是正確使用BackgroundWorker的設計方法:

1.從工具列中將BackgroundWorker拉出來,設置兩個動作事件分別為DoWork、ProgressChanged,另外拉出兩個Button,一個是Start另一個是Stop。

2.在Form的Load事件中必須要設定BackgroundWorker的WorkerSupportsCancellation屬性,確定BackgroundWorker會支援取消作業,也可以點選BackgroundWorker在屬性列中直接更改。


	private void Form1_Load(object sender, EventArgs e)
{
    backgroundWorker1.WorkerSupportsCancellation = true;
}

 

3.在DoWork事件中加入以下程式碼:


	private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    for (; ; )
    {
        try
        {
            backgroundWorker1.ReportProgress(0);
            System.Threading.Thread.Sleep(1000);
        }
        catch (Exception)
        {
            break;
        }
    }
}

 

另外補充Bill Chung補充修改後的程式碼,比較嚴謹,給大家參考:


	private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    for (; ; )
    {
        if (backgroundWorker1.CancellationPending == true)
        {
            e.Cancel = true;
            break;
        }
        else
        {
            try
            {
                backgroundWorker1.ReportProgress(0);
                System.Threading.Thread.Sleep(1000);
            }
            catch (Exception)
            {
                e.Cancel = true;
                break;
            }
        }
    }
}

 

4.另外在ProgressChanged事件中加入程式碼如下:

把所有你想要做的事情都寫在ProgressChanged,但是這裡就不要再寫一個無線迴圈或是會打死結的程式了,不然一樣會卡死在裡頭唷。


	private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    textBox1.Text =DateTime.Now.ToString();
}

 

5.接著在Start與Stop的Click事件中分別啟動與取消BackgroundWorker:

Start


	private void button1_Click(object sender, EventArgs e)
{
    if (this.backgroundWorker1.IsBusy != true)
    {
        this.backgroundWorker1.WorkerReportsProgress = true;
        this.backgroundWorker1.RunWorkerAsync();
    }
}

IsBusy是確認BackgroundWorker是否已經被啟動了,以免重覆啟動造成錯誤。

 

Stop


	private void button2_Click(object sender, EventArgs e)
{
    this.backgroundWorker1.WorkerReportsProgress = false;
    this.backgroundWorker1.CancelAsync();
    this.backgroundWorker1.Dispose();
}

Dispose()是用來釋放BackgroundWorker,其實在這個範例中並不需要使用,但是當做的事情很多時,還是必須要是放一下。

以上就是如何使用BackgroundWorker跨執行緒對控件做控制。

PeterDotNetCS10081901.zip