.net2后是不能跨線程訪問控件的。,窗體上的控件是當前線程創建的,當用戶異步執行一個方法:在該方法中給窗體上的控件賦值,記住:當執行一個異步委托的時候,其實
就是開了一個線程去執行那個方法,這樣就會報錯:線程間操作無效: 從不是創建控件“某某某”的線程訪問它。
C# WinForm開 發中,這是一個比較常見的異常:線程間操作無效,從不是創建控件“xxx”的線程訪問它。這個異常來源於.NET2的一個限制:工作線程不能訪問窗口線程 創建的控件。解決方法主要有兩種,一種是在窗口線程中設置CheckForIllegalCrossThreadCalls = false ;另一種方式比較麻煩,使用委托的方式調用Invoke方法。
public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; }
但以上不是推薦的方法。更好的辦法是用委托解決
private void button1_Click(object sender, EventArgs e) { new Action(show).BeginInvoke(null, null); } void show() { //異步外的方法。這樣窗體不會假死 while (true) { Thread.Sleep(2000); Action ac = new Action(showText); this.Invoke(ac); //在同步方法里面實現更新窗體上的數據 } } /// <summary> /// 更新數據 /// </summary> void showText() { richTextBox1.AppendText("更新\n"); }
或者使用InvokeRequired屬性判斷
/*
// 摘要:
// 獲取一個值,該值指示調用方在對控件進行方法調用時是否必須調用 Invoke 方法,因為調用方位於創建控件所在的線程以外的線程中。
//
// 返回結果:
// 如果控件的 System.Windows.Forms.Control.Handle 是在與調用線程不同的線程上創建的(說明您必須通過 Invoke
// 方法對控件進行調用),則為 true;否則為 false。
private void button1_Click(object sender, EventArgs e) { //new Action(show).BeginInvoke(null, null); new Action(show1).BeginInvoke(null, null); } void show1() { while (true) { Thread.Sleep(2000);//模擬等待效果 show2(); } } void show2() { //說明的當前外部線程 /* // 摘要: // 獲取一個值,該值指示調用方在對控件進行方法調用時是否必須調用 Invoke 方法,因為調用方位於創建控件所在的線程以外的線程中。 // // 返回結果: // 如果控件的 System.Windows.Forms.Control.Handle 是在與調用線程不同的線程上創建的(說明您必須通過 Invoke // 方法對控件進行調用),則為 true;否則為 false。 */ if (InvokeRequired) { /*既然是外部線程,那么就沒有權限訪問主線程上的控件 * 故要主線程訪問,開啟一個異步委托捆綁要執行的方法 * 交給主線程執行 */ Action ac = new Action(show2); this.Invoke(ac); //這里執行后。則InvokeRequired就為false。因為此時已經是主線程訪問當前創建的控件 } else { richTextBox1.AppendText("更新77\n"); } }
看了第一段代碼是不是很不爽的感覺。showText()方法就一條賦值語句,則也獨立成一個方法。這里可以簡化代碼,用匿名函數或者用更簡單的lambda表達式(這個方法不需要其他用戶調用,就可以考慮用匿名函數)。那么看簡化后的代碼
private void button1_Click(object sender, EventArgs e) { new Action(show).BeginInvoke(null, null); } void show() { //異步外的方法。這樣窗體不會假死 while (true) { Thread.Sleep(2000);//在異步方法外實現等待,這樣窗體不會假死 //Action ac = new Action(showText); //this.Invoke(ac); //在同步方法里面實現更新窗體上的數據 //匿名函數 Action at = new Action(delegate(){ richTextBox1.AppendText("更新\n"); }); //lambda表達式更簡單 Action at1 = new Action(()=> { richTextBox1.AppendText("更新\n"); }); this.Invoke(at);
//這里this。代表當前窗體(控件)上的線程執行此方法
//這里不一定是this,只要是當前窗體上的控件都可以,比如
// button1.Invoke(at);
this.Invoke(at);//Invoke:在擁有此控件的基礎窗口句柄的線程上執行指定的委托。
//richTextBox1.Invoke(at);
//button1.Invoke(at);
} }
我們用同樣的方法把InvokeRequired屬性判斷的那段代碼也用lambda表達式簡寫
private void button1_Click(object sender, EventArgs e) { new Action(show1).BeginInvoke(null, null); } void show1() { //while (true) //{ // Thread.Sleep(2000);//模擬等待效果 // show2(); //} for (int i = 0; i < 5000; i++) { Thread.Sleep(2000);//模擬等待效果 //show2(); if (InvokeRequired) { Action ac = new Action(() => { richTextBox1.AppendText("更新767\n"); }); this.Invoke(ac); //這里執行后。則InvokeRequired就為false。因為此時已經是主線程訪問當前創建的控件 } } }
以上兩種方法都是實現相同的效果,差別就是多了一個InvokeRequired判斷是否需要執行Invoke()方法
參考:http://www.cnblogs.com/txw1958/archive/2012/08/21/2649192.html