c# 線程間操作無效: 從不是創建控件“”的線程訪問它,用託管來解決

如果代碼是在VS2003中的話應該不會拋出這個異常,只有在使用VS2005的時候,如果你從非創建這個控件的線程中訪問這個控件或者操作這個控件的話就會拋出這個異常。這是微軟爲了保證線程安全以及提高代碼的效率所做的改進.



以前在VS2003中,我們通常都會忽略這種問題的存在,無限制的使用線程來完成一些工作,但是當系統慢慢變大的時候,這種濫用的線程不但沒有提高我們的效率,反而使我們找錯誤的效率以及系統的複雜度大大的增加了。

微軟的這一改動可以讓我們在使用線程的時候,充分考慮一下線程的安全以及線程的使用規範,這樣會有益於我們做出健壯的程序。

堅決這種跨線程的問題在VS2005中提供了兩種簡單的方法(我個人當前所瞭解的只有這兩種):
一種是通過設置System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;(winform下)如果在你的程序初始化的時候設置了這個屬性,而且在你的控件中使用的都是微軟Framework類庫中的控件的話,系統 就不會再拋出你上面所說的這個錯誤了。當然這只是爲了將VS2003的代碼轉換到VS2005下所使用的一種常見的方法。不建議採用。

第二種方法就是微軟建議採用的跨線程調用的一種通用方法,就是使用代理來實現,就是將你所要操作的代碼放到一個代理種,然後將這個代理交給創建這個控件的線程來執行你的代碼。例如:
private void Form1_Load(object sender, EventArgs e)
{
//創建線程 需要System.Threading命名空間
Thread t1, t2; // 說明爲窗體類成員
t1 = new Thread(new ThreadStart(BackgroundProcess));
t1.Start(); //啓動線程t1
}
/// <summary>
/// 定義一個代理
/// </summary>
private delegate void dd();

private void BackgroundProcess()
{
// 將代理實例化爲一個匿名代理
dd = delegate()
{
int i = 1;
while (true)
{
// 向列表框增加一個項目
listBox1.Items.Add("Iterations: " + i.ToString());


i++;
Thread.Sleep(2000); // 指定線程休眠的時間
}
};
listBox1.Invoke(dd);
}
上面這個代碼只是在你的代碼中聲明瞭一個代 理,並且用VS2005中新加的語法(匿名代理,又名匿名方法。)來實例話這個代理,將你在線程中要操作的代碼都放到這個匿名的方法中去。然後通過使用控 件的Invoke方法(也可以使用控件的BeginInvoke方法——Invoke方法是同步的BeginInvoke方法是異步的)來調用這個代理。 當然在Invoke方法中你也可以輸入任何形式的代理。通過這種方式調用的代碼就不會拋出你出現的那個異常了。

上面這種代理的方式類似於C++中的回調函數,你寫出了執行的方法,然後通知某個線程由那個線程來調用你的這個方法,這樣就做到了在固定的線程裏執行修改線程內部組件的方式。這樣就完全達到線程安全了。

另外,如果你使用的不是.NET類庫中的控件 的話,最好不要使用第一種方式,因爲某些第三方控件在線程安全性方面做的不是太好,可能還會拋出一些不可預料的異常。例如DevExpress控件庫一般 會拋出對象引用爲空的異常。所以建議所有在VS2005種使用線程操作控件的時候都利用第二種方法來實現,如果嫌麻煩,也可以將這些控件利用繼承的方式在 父類裏面實現這些代理方式的修改
發佈了33 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章