Windows多线程与对话框
程 化
Windows的对话框是获取信息输入的主要手段,增加线程则是获得更好UI响应的重要方式。由于Windows在处理对话框时进行线程调度的特殊性,如果不对此加以特别注意,增加线程可能不能带来UI响应的改善。
1 跨线程创建对话框
1.1 需求
有这样的应用场景:创建非模态对话框后需要马上做些耗时的工作,而同时又希望能够立刻在对话框上操作,所以,希望让非模态对话框工作在单独的线程上。
1.2 方案
主线程启动一个UI线程,并且,让这个线程创建非模态对话框。
想法是:既然用单独的线程创建了对话框,所以,主线程在创建UI线程后,就可以继续自己其他耗时的工作了。
1.3 实现
用一个简单的例子程序来试验这个方案:在VC++中创建一个基于SDI的MFC应用程序,在其CView派生类中处理“Call”菜单命令,创建派生自CWinThread的UI线程,在这个UI线程初始化过程中创建一个对话框。
1.3.1 单独的UI线程创建对话框
实现一个派生自CWinThread的UI线程类,其中最关键的是CreateMyDlg和DestroyMyDlg函数。
类如下
class UIWorker : public CWinThread
{
DECLARE_DYNCREATE(UIWorker)
protected:
UIWorker(); // protected constructor used by dynamic creation
// Attributes
public:
// Operations
public:
bool CreateMyDlg( void );
void SetOwnerWnd( HWND hWnd );
void DestroyMyDlg( void );
……
private:
CDlgUserTest* m_pDlgTest;
HWND m_hOwnerWnd;
};
CreateMyDlg和DestroyMyDlg的实现都非常简单
bool UIWorker::CreateMyDlg( void )
{
m_pDlgTest = new CDlgUserTest;
if ( NULL == m_pDlgTest )
{
return false;
}
CWnd* pWnd = NULL;
if ( NULL != m_hOwnerWnd )
{
pWnd = reinterpret_cast<CWnd*>( CWnd::FromHandle( m_hOwnerWnd ));
if ( NULL == pWnd )
{
return false;
}
}
BOOL bSuccess = m_pDlgTest->Create( IDD_DIALOG_TEST, pWnd );
if ( bSuccess )
{
bSuccess = m_pDlgTest->ShowWindow( SW_SHOW );
}
return bSuccess?true:false;
}
void UIWorker::DestroyMyDlg( void )
{
if ( NULL != m_pDlgTest )
{
delete m_pDlgTest;
m_pDlgTest = NULL;
}
}
1.3.2 主线程创建UI线程
主线程就更加简单了。在菜单的对应操作中,创建线程,为了表示主线程继续工作,提供一个循环。
bool CUserView::Call(void)
{
m_pUIWorker = static_cast<UIWorker*>( AfxBeginThread(
RUNTIME_CLASS( UIWorker )));
for ( int i = 0; i < 100000; ++i )
for ( int j = 0; j < 10000; ++j )
;
return true;
}
1.4 结果:奇怪的延迟
希望达到的效果是:
主程序启动后,显示一个单文档视界面,有一个Work菜单
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 6in; HEIGHT: 298.5pt" type="#_x0000_t75"><imagedata o:title="MainUI" src="file:///C:%5CDOCUME~1%5Choward%5CLOCALS~1%5CTemp%5Cmsohtml1%5C03%5Cclip_image001.jpg"></imagedata></shape>
点击Call菜单后,对话框应该马上弹出,显示为:
<shape id="_x0000_i1026" style="WIDTH: 6in; HEIGHT: 298.5pt" type="#_x0000_t75"><imagedata o:title="ShowDialog" src="file:///C:%5CDOCUME~1%5Choward%5CLOCALS~1%5CTemp%5Cmsohtml1%5C03%5Cclip_image003.jpg"></imagedata></shape>
执行中,对话框不会马上弹出,而会等待一定的时间,直到循环结束,CUserView的Call函数返回,对话框才会弹出。等待的时间和循环的长短成正比。
2 问题分析
2.1 不单纯的对话框:要求Windows作特殊处理
对话框是一种很不单纯的窗口。无论是创建、消息分发还是销毁,Windows都会对对话框做一些特殊的处理。如果用SDK进行对话框编程,就会发现创建对话框需要专门的Win32 API。而且,我们查阅平台SDK的讲述时,也会发现对话框需要Windows进行若干额外的“照顾”。事实上,之所以会出现前述的“延迟”情况,就是Windows进行额外协调的结果。
2.2 窗口协调导致等待
2.2.1 Windows协调对话框弹出过程
使用SPY++研究窗口消息,会发现非模态对话框创建时,原来拥有焦点的窗口会收到WM_KILLFOCUS消息,而且获得焦点的窗口是创建的对话框。
2.2.2 线程需要分发消息,不能堵塞
根据例子来看,这个窗口焦点的协调过程被上升到了线程协调的层次。现象就是:如果被去激活的窗口的线程被阻塞,不能立刻处理WM_KILLFOCUS消息的话,创建对话框的线程也会被阻塞,对话框一直不能被显示出来,直到线程不再阻塞,WM_KILLFOCUS被分发和处理。
2.3 解决方案
这个问题产生的原因是:在主线程繁忙的时候有主线程必须要处理的消息,也就是主线程消息循环因为窗口处理函数占用时间过长而被阻塞。因此,这个问题更多是一个设计问题而非技术难点。也许,我们该问的是:
n 我真的要用一个冗长的工作来阻塞主线程这样长的时间吗?
n 我是否可以在单独的一个工作者线程中来处理这个长的工作?
考虑了其他的可能性后,如果对上述问题的答案仍然为“是”的话,可以采取以下解决方案:因为我们缺少的是消息循环,所以,加上消息循环,让对话框能够显示出来之后,再去进行其他工作的处理。
bool CUserView::Call(void)
{
m_pUIWorker = static_cast<UIWorker*>( AfxBeginThread(
RUNTIME_CLASS( UIWorker )));
while ( !m_pUIWorker->GetDoneFlag())
{
MSG msg;
::GetMessage( &msg, this->m_hWnd, NULL, NULL );
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
for ( int i = 0; i < 100000; ++i )
for ( int j = 0; j < 10000; ++j )
;
return true;
}
红色的代码就是加上消息循环。要注意的是,相应的线程类里面应该在显示出对话框后设置一个标志,并且让主线程可以查询到这个标志,从而终止这个临时的消息循环。
3 启示
3.1 Windows线程调度
Windows的线程调度原则对于程序员来说非常简单。这条原则是:程序员无法决定线程调度过程。
只是因为“程序员无法决定线程调度过程”,并不意味着程序员不应该去了解一些特别的线程调度过程。在某些场合下,正如上面在和对话框相关的某些时机,也许,Windows的线程调度是有明确规则的。所以,大多数情况下,程序员可以认为Windows的线程调度对于自己来说是一个黑盒。但是,某些时候,这个盒子中间发生的事情也需要了解和掌握。
3.2 对话框
对话框是一个古老的话题,很多的人仔细讨论了对话框的方方面面。对话框一直是Windows里面非常特殊的一种窗口。它的消息循环,与其他窗口的协调要求,都和普通的窗口有不同之处。因此,为了配合这些不同之处,Windows在线程协调上也做了一些手脚。
3.3 多线程编程更多是一种设计
更重要的启示是:多线程需要更多的考虑设计。
毫无疑问,多线程可以使多种工作并行进行,提高工作效率,改善界面响应。然而,多线程应用中一个麻烦的问题是:决定对哪些工作使用单独的线程。这个决定过程其实就是设计过程。如果设计方案不合理,比如,如本例子中反映出来的问题——在主线程被长时间的工作阻塞的情况下,增加的线程并不会给我们带来明显的响应改善。而且,如果设计方案不合理,会带来更多的“临时机制”的采用,如本例中必须增加一个单独的消息循环,并且需要在两个线程中就对话框是否创建出来进行通讯。这样的实现在很大程度上削减了希望用多线程带来的好处。
分享到:
相关推荐
在Windows编程中,多线程技术常常用于提升应用程序的响应性和效率,特别是在处理耗时操作时,如网络通信、大数据计算或长时间的IO操作。在对话框(Dialog Box)这样的用户界面组件中创建多线程,可以确保主线程...
在VC++编程环境中,MFC(Microsoft Foundation Classes)是一个...通过学习和理解这些代码,开发者能够更好地掌握MFC中线程、对话框、单文档和多文档界面的使用方法,从而开发出更复杂、功能丰富的Windows应用程序。
在本示例中,我们将讨论如何在MFC环境中创建一个非模态对话框,并通过多线程防止其“一闪而逝”的现象。 首先,我们要理解“一闪而逝”的问题。通常,当在主对话框的单击事件中直接创建并显示一个新的非模态对话框...
本文将详细解析一个简单的基于多线程的进度条对话框程序,这是C++编程中的一个基础应用,非常适合初学者理解和实践。 首先,让我们了解“多线程”。在单线程环境中,程序按顺序执行任务,而在多线程环境中,可以...
本教程将详细讲解如何在VC++ 2010环境下创建多线程,并实现一个用于打开文件的对话框。我们将深入探讨以下知识点: 1. **线程基础知识**: - 线程是操作系统中的基本执行单元,它与进程一起构成了并发执行的基础。...
本示例“基于对话框的多线程进度条更新”展示了如何在MFC环境中利用多线程技术来更新对话框中的进度条控件,以实时反映后台任务的执行状态。这对于那些需要在后台执行长时间操作的应用程序来说尤其有用,因为它允许...
本示例项目“MFC进度条样例-多线程+定时器+模式对话框”展示了如何在多线程环境中使用MFC来创建一个模式对话框,实时更新进度条以反映后台操作的进度。 首先,我们要理解什么是模式对话框(Modal Dialog)。模式...
在Windows编程领域,使用对话框(Dialog Box)与Socket结合实现多线程聊天是一种常见的应用场景。这个场景通常涉及网络通信、用户界面交互以及多线程技术。以下将详细阐述相关知识点: 1. **对话框(Dialog Box)**...
在Windows编程环境中,MFC(Microsoft Foundation Classes)是一种基于C++的类库,用于构建桌面应用程序,特别是对话框和窗口。然而,在开发过程中,我们可能会遇到对话框未响应或卡死的问题,这通常是由于主线程被...
通过这个项目,你可以深入理解MFC对话框的创建与管理,以及如何在MFC中使用多线程进行并发任务处理。同时,也会涉及到线程通信、同步和资源管理等重要概念。这不仅有助于提升你的编程技能,也是对实际软件开发能力的...
《Windows多线程编程技术与实例》是一本深入探讨Windows环境下多线程编程的书籍,其配套源代码提供了丰富的实践示例,帮助读者理解和掌握多线程编程的关键技术和实际应用。下面将对这些源文件进行详细解读。 1. **...
在C#编程中,多线程技术是一种常见且强大的工具,用于提高应用程序的性能和响应性。当一个耗时的任务需要执行时,如文件读写、网络请求或复杂的计算,多线程可以使用户界面(UI)保持流畅,不被长时间阻塞。在本教程...
在IT领域,多线程是程序设计中的一个重要概念,尤其在UI(用户界面)与后台处理相结合的应用中,它的作用尤为显著。...正确地实现多线程对话框例程,不仅能让程序运行更加流畅,还能提高用户体验。
11. **多线程**:为了不阻塞主UI线程,文件复制操作可能需要在一个单独的线程中执行。使用`AfxBeginThread()`或`CreateThread()`函数可以创建新线程。 12. **错误处理**:在文件复制过程中,应捕获并处理可能出现的...
在《Windows多线程编程》一文中,作者韩耀旭通过一个简单的示例——一个基于对话框的应用程序`SingleThread`,阐述了单线程程序在处理耗时任务时的局限性。当点击“延时6秒”按钮时,由于该操作在一个单一线程中执行...
MFC(Microsoft Foundation Classes)是微软提供的一种C++类库,它为Windows应用程序开发提供了丰富的支持,包括多线程功能。本示例代码"基于对话框的MFC多线程示例代码"将帮助开发者理解如何在MFC应用中有效地使用...
在VC++编程环境中,有时...总的来说,VC++中同时关闭多个子对话框涉及到对Windows API的熟练掌握,对MFC框架的理解,以及对多线程和消息机制的运用。理解并掌握这些知识点,将有助于开发出更加高效、稳定的用户界面。
3. 多线程编程:为了支持多用户同时在线聊天,可能需要使用多线程技术来处理不同用户的消息。 4. 网络编程:学习TCP/IP协议,实现客户端-服务器模型,进行数据的收发。 5. 数据结构与算法:可能涉及到如何高效地存储...