WinForm(C#)多线程编程并更新界面(UI)
(2013-01-18 20:34:52)
标签:
多线程winforminvoke委托参数it |
分类: C#那些事儿 |
- using
System; - using
System.Collections.Generic; - using
System.ComponentModel; - using
System.Drawing; - using
System.Linq; - using
System.Windows.Forms; - using
System.Threading; -
- namespace
doWorker -
{
-
public partial classForm1 : Form -
{ -
delegate void MyDelegate( intvalue); -
Thread t; -
int i = 0; -
public Form1() -
{ -
InitializeComponent(); -
} -
-
// 在新的线程中做“需要长时间做的”工作 -
private void button1_Click( objectsender, EventArgs e) -
{ -
t = new Thread(doWork); -
t.Start(); -
} -
-
// 要长时间做的工作 -
void doWork() -
{ -
MyDelegate d = new MyDelegate(setValue); -
while ( true) -
{ -
++i; -
-
//---WinForm-- -
this.Invoke(d, i); -
//----WPF---added by wonsoft.cn--- -
this.Dispatcher.Invoke(d, i); -
-
Thread.Sleep(100); -
} -
} -
-
// 更新用户界面 -
void setValue( intvalue) -
{ -
label1.Text = value.ToString(); -
} -
-
// 终止线程的执行 -
private void button2_Click( objectsender, EventArgs e) -
{ -
t.Abort(); -
} -
} - }
当我们利用一个新创建的线程来执行某些花时间的运算时,怎样知道运算进度如何并通过UI反映给用户呢?解决方法很多!比如熟悉多线程编程的用户很快会想到,我们采用一些低级的同步方法,工作者线程把状态保存到一个同步对象中,让UI线程轮询(Polling)该对象并反馈给用户就可以了。不过,这还是挺麻烦的,实际上不用这样做,Control类(及其派生类)对象有一个Invoke方法很特别,这是少数几个不受线程限制的成员之一。我们前面说到,绝对不要在任何其他线程里面调用非本线程创建的控件的成员时,也说了“只有极个别情况例外”,这个Invoke方法就是极个别情况之一----Invoke方法可以从任何线程里面调用。下面我们来讲解Invoke方法。
Invoke方法的参数很简单,一个委托,一个参数表(可选),而Invoke方法的主要功能就是帮助你在UI线程(即创建控件的线程)上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。不管当前线程是不是UI线程,Invoke都阻塞直到委托指向的方法执行完毕,然后切换回发出调用的线程(如果需要的话),返回。注意,使用Invoke方法时,UI线程不能处于阻塞状态。以下MSDN里关于Invoke方法的说明:
“控件上有四种方法可以安全地从任何线程进行调用:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
说完Invoke,顺便说说BeginInvoke,毫无疑问这是Invoke的异步版本(Invoke是同步完成的),不过大家不要和上面的System.Windows.Forms.MethodInvoker委托中的BeginInvoke混淆,两者都是利用不同线程来完成工作,但是控件的BeginInvoke方法总是使用UI线程,而其他的异步委托调用方法则是利用线程池里的线程。相对Invoke而言,使用BeginInvoke稍稍麻烦一点,但还是那句话,异步比同步效果好,尽管复杂些。比如同步方法可能出现这样一种死锁情况:工作者线程通过Invoke同步调用UI线程里的方法时会阻塞,而万一UI线程正在等待工作者线程做某件事时怎么办?因此,能够使用异步方法时应尽量使用异步方法。
最近做一个程序时遇到了这样的问题,其中点击一个按钮之后需要进行长时间的处理工作,但是在处理过程中,UI就停止更新了(我这里是socket接收客户端消息显示在界面),而且处理完成后,才会更新,最可恶的是更新出来的竟然是几条一样的消息,且是最后发送的那条消息的内容。因此,需要多线程。
下面的代码是我更改之后的应用在我的程序里的,需改后可以满足我的需求了