单线程的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有效)
using
System;
using
System.Threading;
using
System.Windows.Forms;
namespace
ThreadTest
{
public
partial
class
Form1 : Form
{
delegate
void
D(
object
obj);
public
Form1()
{
InitializeComponent();
}
private
void
btnSet_Click(
object
sender, EventArgs e)
{
Thread t
=
new
Thread(
new
ParameterizedThreadStart(SetTextBoxValue));
t.Start(
"
Hello World
"
);
}
void
SetTextBoxValue(
object
obj)
{
if
(textBox1.InvokeRequired)
{
D d
=
new
D(DelegateSetValue);
textBox1.Invoke(d,obj);
}
else
{
this
.textBox1.Text
=
obj.ToString();
}
}
void
DelegateSetValue(
object
obj)
{
this
.textBox1.Text
=
obj.ToString();
}
}
}
3.利用SynchronizationContext上下文 -- 最神秘的方法(Winform/Silverlight能用)
之所以说它神秘,是因为msdn官方对它的解释据说也是不清不楚
using
System;
using
System.Threading;
using
System.Windows.Forms;
namespace
ThreadTest
{
public
partial
class
Form1 : Form
{
public
Form1()
{
InitializeComponent();
}
private
void
btnSet_Click(
object
sender, EventArgs e)
{
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)
{
this
.textBox1.Text
=
obj.ToString();
}
}
public
class
MyPram
{
public
SynchronizationContext context {
set
;
get
; }
public
object
parm {
set
;
get
; }
}
}
4.利用BackgroundWorker --最偷懒的办法(Winform/Silverlight通用)
BackgroundWorker会在主线程之外,另开一个后台线程,我们可以把一些处理放在后台线程中处理,完成之后,后台线程会把结果传递给主线程,同时结束自己。
using
System;
using
System.ComponentModel;
using
System.Windows.Forms;
namespace
ThreadTest
{
public
partial
class
Form1 : Form
{
public
Form1()
{
InitializeComponent();
}
private
void
btnSet_Click(
object
sender, EventArgs e)
{
//
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)
{
//
MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
e.Result
=
e.Argument;
//
这里只是简单的把参数当做结果返回,当然您也可以在这里做复杂的处理后,再返回自己想要的结果(这里的操作是在另一个线程上完成的)
}
void
bw_RunWorkerCompleted(
object
sender, RunWorkerCompletedEventArgs e)
{
//
这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了
this
.textBox1.Text
=
e.Result.ToString();
//
MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}
5.Dispatcher.BeginInvoke--Silverlight的独门秘籍
代码
using
System.Threading;
using
System.Windows.Controls;
using
System.Windows.Input;
namespace
ThreadTest
{
public
partial
class
MainPage : UserControl
{
public
MainPage()
{
InitializeComponent();
}
private
void
LayoutRoot_MouseLeftButtonDown(
object
sender, MouseButtonEventArgs e)
{
Thread t
=
new
Thread(SetTextValue);
t.Start(
"
Hello World
"
);
}
void
SetTextValue(
object
text)
{
this
.Dispatcher.BeginInvoke(()
=>
{
this
.txt.Text
=
text.ToString(); });
}
}
}
转载请注明来自菩提树下的杨过http://www.cnblogs.com/yjmyzz/archive/2009/11/25/1610253.html