快速业务通道

浅谈.NET下的多线程和并行计算(八)Winform中多线程编程基础 上

作者 佚名技术 来源 NET编程 浏览 发布时间 2012-05-20

首先我们创建一个Winform的应用程序,在上面添加一个多行文本框和一个按钮控件,按钮的事件如下 :

Thread.Sleep(1000);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
   sb.Append("test");
string s = sb.ToString();
textBox1.Text = s;

首先我们可以把这个操作理解为一个非常耗时的操作,它至少占用1秒的时间。在1秒后,我们整了一 个大字符串作为文本框的值,然后在标签上显示给文本框赋值这个UI渲染行为需要的时间,程序执行结果 如下:

我们可以感受到,在点击了按钮之后整个程序的UI就卡住了,没有办法拖动没有办法改变大小,用于 体验非常差。一般能想到会新建一个线程来包装这个方法,使得UI线程不被卡住:

new Thread(() =>
{
   Thread.Sleep(1000);
   StringBuilder sb = new StringBuilder();
   for (int i = 0; i < 10000; i++)
     sb.Append("test");
   string s = sb.ToString();
   textBox1.Text = s;
}).Start();

使用调试方式运行程序的话会得到如下的异常(非调试方式不会):

虽然我们知道这样设置:

Control.CheckForIllegalCrossThreadCalls = false;

可以屏蔽这个错误,但是在非创建控件的线程去更新控件的状态的做法会导致很多问题,比如死锁和 控件部分被更新等。微软推荐我们使用Control的Invoke或BeginInvoke方法来把涉及到控件状态更新的操 作让UI线程去做:

new Thread(() =>
{
   Invoke(new Action(() =>
   {
     Thread.Sleep(1000);
     StringBuilder sb = new StringBuilder();
     for (int i = 0; i < 10000; i++)
       sb.Append("test");
     string s = sb.ToString();
     textBox1.Text = s;
   }));
}).Start();

你可能会想到这么写,但是运行程序后可以发现界面依然是卡死。想一下,虽然我们新开了一个线程 ,但是马上又把整个代码段交给UI线程去做了,当然起不到效果。其实这个方法的工作可以分为两部分, 一部分是我们数据的计算,一部分是把计算好的数据显示在界面上,我们只应该把真正和UI相关的操作放 到 Invoke中让UI线程去做:

new Thread(() =>
{
   Thread.Sleep(1000);
   StringBuilder sb = new StringBuilder();
   for (int i = 0; i < 10000; i++)
     sb.Append("test");
   string s = sb.ToString();
   Invoke(new Action(() =>
   {
     textBox1.Text = s;
   }));
}).Start();

再测试一次可以发现,UI在前1秒多的时间没有卡死,在最后的一点时间还是卡死了。在继续研究卡死 问题之前我们来看一下,Control提供了InvokeRequired属性来让我们判断当前线程是不是UI线程,或者 说当前的操作是否需要进行Invoke:

textBox1.Text = this.InvokeRequired.ToString();
new Thread(() =>
{
   textBox1.Text += Environment.NewLine + this.InvokeRequired.ToString();
   Invoke(new Action(() =>
   {
     textBox1.Text += Environment.NewLine + this.InvokeRequired.ToString();
   }));
}).Start();

通过非调试方式启动程序可以得到如下结果:

很明显:

1) 在线程外的赋值不需要Invoke(在UI线程)

2) 在线程内的赋值需要Invoke(不在UI线程)

3) 在Invoke中的赋值已经封送给UI线程,所以不需要Invoke

继续研究卡死问题,您可能会想到,Control还提供了一个BeginInvoke方法,我们来试试看:

new Thread(() =>
{
   Thread.S

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号