學習Thread的時候,發現了一個問題:如下代碼,意在實現填充listbox的動作在新建的thread中工作,這樣不至於出現UI“假死”現象。
看代碼:
{
listBox1.Items.Clear();
for (int i = 0; i < 10000; i++)
{
listBox1.Items.Add(i.ToString());
}
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(foo));
thread.Start();
}
執行出現錯誤:正在訪問的Control listbox1的線程並不是創建它的線程,也就是不一致(好像是這個意思),一查Msdn,才發現這樣的作業方式是Unsafe的,原因說了一大堆。推薦的方式是首先判斷兩個ThreadID是否一致,如果不一致,採用委託的方式進行safe的編程。
public void foo()
{
if (listBox1.InvokeRequired)
{
Mydel del = new Mydel(foo);
this.Invoke(del);
}
else
{
listBox1.Items.Clear();
for (int i = 0; i < 10000; i++)
{
listBox1.Items.Add(i.ToString());
}
}
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(foo));
thread.Start();
}
不知道是我寫的有問題,還是其他什麼原因,總之這樣是可以運行,但是好像不能達到我的目的。
於是就看到了BackgroundWorker 組件:(msdn定義)BackgroundWorker 類別可以讓您在個別的專用執行緒上執行作業。執行耗費時間的作業 (例如下載和資料庫異動) 時,可能會讓使用者介面 (UI) 看起來像是已經停止反應。當您想要讓 UI 有所反應,但卻又必須面臨與這類作業相關的長時間延遲現象時,BackgroundWorker 類別可以提供便利的方案。
若要設定背景作業,請加入 DoWork 事件的事件處理常式。在這個事件處理常式中呼叫耗時作業。若要啟動作業,請呼叫 RunWorkerAsync。若要接收進度更新的告知,請處理 ProgressChanged 事件。若要在作業完成時接收告知,請處理 RunWorkerCompleted 事件。
您必須小心,不要在 DoWork 事件處理常式中操作任何使用者介面物件。相反地,請透過 ProgressChanged 和 RunWorkerCompleted 事件,與使用者介面通訊。
看例子:
{
double result=0;
Thread.Sleep(5000);
for (int i = 0; i < 100000; i++)
{
result += i;
}
return result;
}
private void button3_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(); //1.開始異步作業,跳轉DoWork事件
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage; //結合ProgressBar顯示執行進度
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = e.Result.ToString(); //4.將作業結果返回UI
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result= foo2(); //2.執行背景運算作業,並存儲結果(注意,不能在這裏與UI進行交互)
}
運行後,可以看到,執行期間,並不影響你在UI做其他的動作,真正實現了異步作業。