由於 Windows 窗體控件本質上不是線程安全的。因此如果有兩個或多個線程適度操作某一控件的狀態(set value),則可能會迫使該控件進入一種不一致的狀態。還可能出現其他與線程相關的 bug,包括爭用和死鎖的情況。於是在調試器中運行應用程序時,如果創建某控件的線程之外的其他線程試圖調用該控件,則調試器會引發一個 InvalidOperationException
本文用一個很簡單的示例來講解這個問題(在窗體上放一個TextBox和一個Button,點擊Button後,在新建的線程中設置TextBox的值)
解決辦法一: 關閉該異常檢測的方式來避免異常的出現
經過測試發現此種方法雖然避免了異常的拋出,但是並不能保證程序運行結果的正確性 (比如多個線程同時設置TextBox1的Text時,很難預計最終TextBox1的Text是什麼)
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Data;
5
using System.Drawing;
6
using System.Text;
7
using System.Windows.Forms;
8
using System.Threading;
9
10
namespace winformTest
11
{
12
public partial class Form1 : Form
13
{
14
public Form1()
15
{
16
InitializeComponent();
17
Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵
18
}
19
20
21
private void button1_Click(object sender, EventArgs e)
22
{
23
SetTextBoxValue();
24
}
25
26
void SetTextBoxValue()
27
{
28
TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method1");
29
ThreadStart TS = new ThreadStart(tbsv.SetText);
30
Thread T = new Thread(TS);
31
T.Start();
32
}
33
34
35
class TextBoxSetValue
36
{
37
private TextBox _TextBox ;
38
private string _Value;
39
40
public TextBoxSetValue(TextBox TxtBox, String Value)
41
{
42
_TextBox = TxtBox;
43
_Value = Value;
44
}
45
46
public void SetText()
47
{
48
_TextBox.Text = _Value;
49
}
50
}
51
}
52
}
解決辦法二:通過委托安全調用 1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Data;
5
using System.Drawing;
6
using System.Text;
7
using System.Windows.Forms;
8
9
namespace winformTest
10
{
11
public partial class Form2 : Form
12
{
13
public Form2()
14
{
15
InitializeComponent();
16
}
17
18
19
private void button1_Click(object sender, EventArgs e)
20
{
21
SetTextBoxValue();
22
}
23
24
25
private delegate void CallSetTextValue();
26
//通過委托調用
27
void SetTextBoxValue()
28
{
29
TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method2");
30
if (tbsv.TextBox.InvokeRequired)
31
{
32
CallSetTextValue call = new CallSetTextValue(tbsv.SetText);
33
tbsv.TextBox.Invoke(call);
34
}
35
else
36
{
37
tbsv.SetText();
38
}
39
}
40
41
42 class TextBoxSetValue
43
{
44
private TextBox _TextBox;
45
private string _Value;
46
47
public TextBoxSetValue(TextBox TxtBox, String Value)
48
{
49
_TextBox = TxtBox;
50
_Value = Value;
51
}
52
53
public void SetText()
54
{
55
_TextBox.Text = _Value;
56
}
57
58
59
public TextBox TextBox
{
60
set
{ _TextBox = value; }
61
get
{ return _TextBox; }
62
}
63
}
64
}
65
}
第三解決辦法:
利用BackgroundWorker控件 1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Data;
5
using System.Drawing;
6
using System.Text;
7
using System.Windows.Forms;
8
using System.Threading;
9
10
namespace winformTest
11
{
12
public partial class Form3 : Form
13
{
14
public Form3()
15
{
16
InitializeComponent();
17
}
18
19
private void button1_Click(object sender, EventArgs e)
20
{
21
using (BackgroundWorker bw = new BackgroundWorker())
22
{
23
bw.RunWorkerCompleted += SetTextBoxValue;
24
bw.RunWorkerAsync();
25
}
26
}
27
28
void SetTextBoxValue(object sender, RunWorkerCompletedEventArgs e)
29
{
30
TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method3");
31
tbsv.SetText();
32
}
33
34
35
class TextBoxSetValue
36
{
37
private TextBox _TextBox;
38
private string _Value;
39
40
public TextBoxSetValue(TextBox TxtBox, String Value)
41
{
42
_TextBox = TxtBox;
43
_Value = Value;
44
}
45
46
public void SetText()
47
{
48
_TextBox.Text = _Value;
49
}
50
}
51
52
}
53
}