執行緒訪問UI控制元件和Control.CheckForIllegalCrossThreadCalls屬性
概況:
多執行緒程式中,新建立的執行緒不能訪問UI執行緒建立的視窗控制元件,這時如果想要訪問視窗的控制元件,發現無法對其控制。這時可將視窗建構函式中的CheckForIllegalCrossThreadCalls設定為false;然後就能安全的訪問窗體控制元件。如果捕獲了對錯誤執行緒的呼叫,則為true;否則為false。System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;程式初始化時設定這個屬性,再使用微軟Framework類庫中的控制元件,系統將不會再丟擲你執行緒的異常資訊。
引用方法:
private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
}
C#的Winform開發中,一般是不推薦使用執行緒去直接訪問UI控制元件的。
訪問 Windows 窗體控制元件本質上不是執行緒安全的。如果有兩個或多個執行緒操作某一控制元件的狀態,則可能會迫使該控制元件進入一種不一致的狀態。還可能出現其他與執行緒相關的 bug,包括爭用情況和死鎖。但是有時在程式編寫的時候寫了一些執行緒直接訪問UI控制元件的程式碼,執行時也不會報錯,就以為這樣做是可以的。比如下面的程式碼,定義一個執行緒,並且在執行是訪問進度條控制元件。執行程式碼後發現,一切都ok,沒有任何異常丟擲。
private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(() => { try { for (int i = 1; i <= 100; i++) { progressBar1.Value = i; Thread.Sleep(100); } } catch (Exception ex) { MessageBox.Show(ex.Message); } }); t.Start(); }
但是在除錯的時候,卻會捕獲一個異常InvalidOperationException,並提示訊息:“執行緒間操作無效,從不是建立控制元件的執行緒訪問它。”這點困惑了很多人。其實這一切都是和Control.CheckForIllegalCrossThreadCalls有關。注意: Control.CheckForIllegalCrossThreadCalls屬性是在 .NET Framework 2.0 版中新增的。它的作用是獲取或設定一個值,該值指示是否捕獲對錯誤執行緒的呼叫。如果設為true則會捕獲對錯誤執行緒的呼叫,反之亦反。
如果一個執行緒並沒有建立控制元件A而去訪問控制元件A,有時候會很幸運沒什麼錯誤,但是在一些複雜的情況下會導致不可以預料的錯誤。因此將 CheckForIllegalCrossThreadCalls 設定為 true 可以更容易地查詢並診斷此執行緒活動。CheckForIllegalCrossThreadCalls會在Control類的靜態構造方法中呼叫如下的語句,Debugger.IsAttached的值表示偵錯程式是否附到程序中:
static Control()
{
//...
checkForIllegalCrossThreadCalls = Debugger.IsAttached;
//...
}
因此,當debug的時候,控制元件的checkForIllegalCrossThreadCalls是true。但是執行的時候checkForIllegalCrossThreadCalls是false。
我們可以手動的設定該值,以此控制是否需要捕獲異常。如下的程式碼:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
showCurrentCheckForIllegalCrossThreadCallsValue();
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{
try
{
for (int i = 1; i <= 100; i++)
{
progressBar1.Value = i;
Thread.Sleep(100);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
});
t.Start();
}
private void showCurrentCheckForIllegalCrossThreadCallsValue()
{
label1.Text = "ProgressBar's CheckForIllegalCrossThreadCalls Value:" + ProgressBar.CheckForIllegalCrossThreadCalls.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
ProgressBar.CheckForIllegalCrossThreadCalls = true;
showCurrentCheckForIllegalCrossThreadCallsValue();
}
private void button3_Click(object sender, EventArgs e)
{
ProgressBar.CheckForIllegalCrossThreadCalls = false;
showCurrentCheckForIllegalCrossThreadCallsValue();
}
}
程式執行的時候(非除錯),ProcessBar當前的CheckForIllegalCrossThreadCalls為False,手動設定為True後,再呼叫執行緒訪問UI,得到了異常。
參考: