開發winform的時後,常會遇到一個問題「Additional information: 跨執行緒作業無效: 存取控制項 'textBox1' 時所使用的執行緒與建立控制項的執行緒不同。」
上述簡單的一段話,卻包含了許多需要理解的東西才能得到解決的辦法,當然直接去找高手們的文章便可解決,但不瞭解原由以後仍會不知所措。
首先用簡單的範例模擬這個問題發生
private void button9_Click(object sender, EventArgs e)
{
textBox1.Text = "";
//在主執行緒上對文字方塊印出個A
textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
var ts = Task.Factory.StartNew(() =>
{
//在副執行緒上對文字方塊印出個B
var thisthread = Thread.CurrentThread.ManagedThreadId;
textBox1.Text += "B(" + thisthread + "):" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
});
//在主執行緒上對文字方塊印出個C
textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
}
很快的就會看到紅紅的錯誤,出錯的原因講的直白點是 - A和C都在主執行緒上跑,現在B你要從其他的執行緒來,我不答應。 (大概是將)
解法在網路上也很多,但正確的解法是透過委派(Delegate)的方式:使B能透過一個調解者(Delegate)進來主執行緒,因此調整一下程式。
//新增一個委派
private delegate void predicate(string s);
private void button9_Click(object sender, EventArgs e)
{
textBox1.Text = "";
textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
//宣告委派
predicate pd = changetext;
var ts = Task.Factory.StartNew(() =>
{
//此副執行緒的id,可用來識別是否和主執行緒不同
var tid = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(1000);
//透過invoke執行被委派的方法
this.Invoke(pd, new object[] { "B(" + tid + ")" });
});
textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
}
private void changetext(string s)
{
textBox1.Text += s + ":" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
}
此時會看到的結果如下:
A:10
C:10
B(12):10
括號內的就是副執行緒的id,冒號後的為主執行緒的id,透過委派的方法,可以使B透過主執緒來進行動作。這樣就解決了跨不同執行緒的問題
接著透過action委派的方法,調整一下委派調用,可以少寫一些code:
private void button9_Click(object sender, EventArgs e)
{
textBox1.Text = "";
textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
var ts = Task.Factory.StartNew(() =>
{
var tid = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(5000);
//無傳回值的委派,無須像delegate一樣需要聲明
this.Invoke(new Action(() =>
{
textBox1.Text += "B(" + tid + "):" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
}));
});
textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
}
講的很粗略如有不對之處再請大大們不吝和我說。