在一个Windows窗体应用程序中使用多线程,它具有实际的意义,同时尽量使事情简单
作者:Jason Clark 来源:论坛 2007年11月13日
关键字:
线程和Windows用户界面
Windows Forms类库建立在大家所熟知的User32 Win32 API 基础上。User32实现了GUI的基本元素,例如窗体,菜单及按钮之类等。所有由User32实现的窗体和控件都使用了事件驱动型结构。
这里简单的讲讲它们如何工作。发生在窗体上的事情,例如鼠标单击,坐标变化,大小变化和重绘请求,都称作事件。在User32 API模型中的事件是由窗体消息表示的。每一个窗体有一个函数,叫做窗口过程或WndProc,它由应用程序实现。WndProc为窗体负责处理窗体消息。
但是WndProc不是神奇的被系统调用。相反,应用程序必须调用GetMessage主动地从系统中得到窗体消息。该消息被应用程序调用DispatchMethod API方法分配到它们的目标窗体的WndProc方法中。应用程序只是简单的循环接收和分配窗口消息,一般叫做消息泵或消息循环。线程拥有所有窗体,这样它就可以提取消息,WndProc函数也被同样的线程所调用。
现在回到Windows Forms类来。Windows Forms在应用程序中对User32的消息结构进行了大约95%的抽象。代替了WndProc函数,Windows Forms程序定义了事件处理器和虚拟函数重载来处理与窗体(窗口)或控件有关的不同系统事件。然而消息提取必须要运行,它在Windows Forms API的Application.Run方法里面实现。
Figure 1 所示的代码似乎仅仅调用了Application.Run接着就退出了。 然而这缺少了透明性:应用程序的主线程在其生命周期里只对Application.Run进行一次调用进行消息提取,其结果却为用应用程序其它部分创造了不同事件处理器的调用。当窗体上的按钮被单击时,在Figure 1 中的OnClick方法被主线程调用,该线程同样要负责在Application.Run中提取消息。
这解释了为什么在一个长操作发生时,用户交互没有响应。如果在一个事件处理器中一个很长的操作 (如数据库查询)发生了,那么主线程就被占用,它又需要不断提取消息。没有能力提取消息并发送到窗口或窗体上, 就没有能力响应调整大小,重绘自己,处理单击或响应用户的任何交互。
在接下来的部分为了执行长操作我将使用公共语言运行时的线程池来修改Figure 1 所示的例子代码,这样主线程仍然可以提取消息。