單線程的winfom程式中,設定一個控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一個新線程中這麼做,比如:
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
//當然也可以用匿名委托寫成Thread t = new Thread(SetTextBoxValue);
t.Start("Hello World");
}
void SetTextBoxValue(object obj)
{
this.textBox1.Text = obj.ToString();
運作時,會報出一個無情的錯誤:
線程間操作無效: 從不是建立控件“textBox1”的線程通路它。
究其原因,winform中的UI控件不是線程安全的,如果可以随意在任何線程中改變其值,你建立一個線程,我建立一個線程,大家都來搶着更改"TextBox1"的值,沒有任何秩序的話,天下大亂...
解決辦法:
1.掩耳盜鈴法(Control.CheckForIllegalCrossThreadCalls = false;)--僅Winform有效
using System;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵
}
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
t.Start("Hello World");
void SetTextBoxValue(object obj)
this.textBox1.Text = obj.ToString();
}
}
設定Control.CheckForIllegalCrossThreadCalls為false,相當于不檢測線程之間的沖突,允許各路線程随便亂搞,當然最終TextBox1的值到底是啥難以預料,隻有天知道,不過這也是最省力的辦法
2.利用委托調用--最常見的辦法(僅WinForm有效)
{
delegate void D(object obj);
InitializeComponent();
if (textBox1.InvokeRequired)
{
D d = new D(DelegateSetValue);
textBox1.Invoke(d,obj);
}
else
this.textBox1.Text = obj.ToString();
void DelegateSetValue(object obj)
3.利用SynchronizationContext上下文 -- 最神秘的方法(Winform/Silverlight能用)
之是以說它神秘,是因為msdn官方對它的解釋據說也是不清不楚
}
Thread t = new Thread(new ParameterizedThreadStart(Run));
MyPram _p = new MyPram() { context = SynchronizationContext.Current, parm = "Hello World" };
t.Start(_p);
void Run(object obj)
MyPram p = obj as MyPram;
p.context.Post(SetTextValue, p.parm);
void SetTextValue(object obj)
public class MyPram
public SynchronizationContext context { set; get; }
public object parm { set; get; }
4.利用BackgroundWorker --最偷懶的辦法(Winform/Silverlight通用)
BackgroundWorker會在主線程之外,另開一個背景線程,我們可以把一些處理放在背景線程中處理,完成之後,背景線程會把結果傳遞給主線程,同時結束自己。
using System.ComponentModel;
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
using (BackgroundWorker bw = new BackgroundWorker())
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync("Hello World");
void bw_DoWork(object sender, DoWorkEventArgs e)
e.Result = e.Argument;//這裡隻是簡單的把參數當做結果傳回,當然您也可以在這裡做複雜的處理後,再傳回自己想要的結果(這裡的操作是在另一個線程上完成的)
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
//這時背景線程已經完成,并傳回了主線程,是以可以直接使用UI控件了
this.textBox1.Text = e.Result.ToString();
}
5.Dispatcher.BeginInvoke--Silverlight的獨門秘籍
代碼
using System.Windows.Controls;
using System.Windows.Input;
public partial class MainPage : UserControl
public MainPage()
private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
Thread t = new Thread(SetTextValue);
void SetTextValue(object text)
this.Dispatcher.BeginInvoke(() => { this.txt.Text = text.ToString(); });