当前位置: 首页 > 图文教程 > 网络编程 > ASP.NET > 处理WinForm多线程程序时的陷阱

ASP.NET
ASP.NET开发:简化应用程序的开发支持Web标准
asp.net XMLHttpRequest实现用户注册前的验证
asp.net 页面间传值方法小结
asp.net url重写浅谈
asp.net 验证码生成和刷新及验证
C#精髓 GridView72大绝技 学习gridview的朋友必看
实例说明asp.net中的简单角色权限控制
asp.net网站开发包wq.dll打包下载
js与ASP.NET 中文乱码问题
asp.net checkbox 动态绑定id GridView删除提示
asp.net TextBox回车触发事件 图片在img显示
asp.net 脏字典过滤问题 用正则表达式来过滤脏数据
asp.NET 脏字过滤算法
asp.NET 脏字过滤算法 修改版
asp.net sql 数据库处理函数命令
asp.net Javascript 的几种写法与提示
ASP.NET MVC学习笔记
asp.net 中国身份证号码验证代码 非正则
Asp.net中使用Sqlite数据库的方法
asp.net 中文字符串提交乱码的解决方法

ASP.NET 中的 处理WinForm多线程程序时的陷阱


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-11-03   浏览: 313 ::
收藏到网摘: n/a

与所有的UI开发平台一样,.NET下线程开发图形界面同样要遵循一个基本原则:就是对UI对象的操作一定要在产生该UI对象的线程里进行(该线程称作UI线程),因为大部分UI对象都不是线程安全的。
在.NET中,把调用调用放在UI线程里执行是通过Form类及其子类的Invoke()方法实现的(具体的过程请参考其他资料),可以这样做是因为Form对象保存了创建它的线程的信息,而且Form类有一个bool类型的属性InvokeRequired,可以通过它查看当前线程是否为创建该Form对象的线程(UI线程)——如果为true,则表示当前线程不是UI线程,反之则是。下面提供一个例子:
using System.Threading;
using System.Windows.Forms;
namespace csharpTest
{
public class TestForm : Form
{
private Form form1;
private Form form2;
public static void Main()
{
TestForm tf = new TestForm();
tf.Show();
tf.UIThread();
Application.Run();
}
public void UIThread()
{
form1 = new Form();
form2 = new Form();
form2.Show();//这里是关键
form1.Show();
Thread thread = new Thread(new ThreadStart(WorkerThread));
thread.Start();
}
public void WorkerThread()
{
if (form2.InvokeRequired)
form2.Invoke(new MethodInvoker(WorkerThread));

else
{
form1.Text = "This is from WorkerThread.";
}
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing (e);
Application.Exit();
}
}
}
TestForm里有两个需要注意的方法,UIThread——用来模拟UI线程,WorkerThread——用来模拟用户线程,UIThread中实例化了成员form1与form2,并调用了它们的Show方法,在WorkerThread中改变form1的Text属性。请注意WorkerThread里有个技巧, if (form2.InvokeRequired) 即如果当前线程不是创建该form2的线程,则将方法通通过过Invoke方法放到UI线程里去执行。但就是这里问题出现了。form1和form2都是在UIThread里建立的,所以它们保存的线程的信息应该是一样的。所以form1.InvokeRquired和form2.InvokeRquired的值在任何线程里都是一样的,即在WorkerThread中InvokeRquire的值都应该是true(因为在不同的线程里)。但是如果注释掉form2.Show()的话form2.InvokeRquired在WorkerThread中的值却是false(在vs.net中调试看到),怎么会这样呢?而且如果不经过判断直接在WorkerThread里调用form2对象的Invoke的话…………居然会抛出异常——“在创建窗口句柄之前,不能在控件上调用 Invoke 或 InvokeAsync”
分析一下该异常的信息,在win32里每一个窗体都有一个窗体句柄,是该窗体在建立时系统分配的,但我们确实在UI线程里建立了form2对象的。这里有个误区.Net里的Form对象并不是和win32的窗体对象完全对应的。本人窃以为,产生一个Form类的实例时,只是产生了一个内存中的普通的对象,并不产生系统窗体(好像叫做User对象吧),只有它第一次呈现在屏幕上(或称作创建)时,才产生系统里表示窗体的User对象且分配句柄,对应的WIN32 API的CreateWindow()方法大概也在这个时候执行(先声明:本人对WIN32 AP 并不熟悉,所以这里如果有什么不妥的话请大家指正)
只有.NET里的form对象调用某种方法使系统产生真正的窗体时,form才会有创建它的线程的信息,且InvokeRquired才有效,即才能调用form的Invoke方法。不过我还没弄清楚哪几个方法可以做到。据我所知Show, CreateGraphics可以产生系统真正的系统窗体。