返回首页

简介
虽然ATL和WTL提供了一个Win32处理可以参照的各种对象的最有用的包装类,线程仍然必须创建和控制使用的API函数和处理直接。诚然,这是不是一个很难的事情,但一个干净的面向对象的设计往往可以帮助避免在多线程应用程序中的错误。
类的集合,我会到目前经常被证明是非常有用的我。它包含了处理线程,以及包装,帮助落实工作人员和GUI线程的基类。线程句柄的包装类
没有太多说这些类。在通常由WTL的风格,CThreadT是围绕一个线程处理的包装模板类,它提供了大多数Win32 API函数,作为方法的线程句柄。
更方便的是,一个通常使用的模板实例CThread和CThreadHandle:CThread将关闭线程句柄,当它被破坏,而CThreadHandle不会,。创建一个线程
实现为静态方法CThreadT,创建线程:创建,这是一个包装周围_beginthreadex(或的CreateThread,如果最小的CRT使用"选项"设置)/ / ... ...。CThread线程= CThread:((LPTHREAD_START_ROUTINE)  60; MyThreadProc,pParam);/ / ...CThread和CThreadHandle其他方法CThreadT(句柄= NULL,DWORD = 0)环绕一个CThread实例给定的线程句柄和线程ID。还有一个拷贝构造函数,这将在给定的句柄调用DuplicateHandle。线程句柄和ID可以检索GetHandle()的getId()。方法打开,GetPriority,SetPriority,GetExitCode,GetThreadTimes,IsIOPending,恢复,暂停,终止和退出都是围绕各自的API函数,它们的名字大多是"线程"这个词的净包装。该方法加入执行WaitForSingleObject的线程句柄。GUI线程
GUI线程是从普通工人中,他们有一个消息队列的线程不同。这意味着,我们可以将邮件投递到一个GUI线程使用PostThreadMessage。
模板类CGuiThreadT,和它的实例CGuiThread,并CGuiThreadHandle,就像CThreadT类,为此额外的方法PostThreadMessage。
造成一个GUI线程退出,通常是一个WM_QUIT消息发布到自己的队列。这是通过调用PostQuitMessage方法。但是,这种方法不换行的同名的API,因为后者职位WM_QUIT调用线程,这并不总是相同。线程执行类
作为ATL和WTL往往区分一个对象和它的执行情况(比较CWindow的和CWindowImpl的)的句柄,我选择了我的线程类的设计(虽然两种情况都是不完全可比)。 CThreadImpl类提供了一个线程"实施"类的骨架。
来自CThreadImpllt线程类; TGT;和执行Run方法:类CWorkerThread:公共CThreadImpllt; CWorkerThreadgt;{市民: 运行的DWORD() {  ; / /做一些有用的东西... ... 返回0; }};/ // /在一些其他的功能,那就是从你的主线程调用:CWorkerThread * PTHREAD =新CWorkerThread;
运行的返回值是线程的退出代码,就像在标准的Win32 ThreadProc。
如果您创建了一个CWorkerThread实例,它会立即开始投放。如果你想启动它后,你可以传递CREATE_SUSPENDED CThreadImpl的构造函数,调用resume后。
注意,在创建线程的构造的运行,所以线程初始化一些可能被转移到运行方法。 ExampleClass的CWorkerThread:公共CThreadImpllt; CWorkerThreadgt;{ CWorkerThread() CThreadImpllt; CWorkerThreadgt;(CREATE_SUSPENDED) {} 布尔初始化() { / /执行初始化。 返回TRUE; } 0; 运行的DWORD() { 如果(!初始化())  60; 返回1; / /做一些有用的东西... ...  60; 返回0; }};/ // /在一些其他的功能,那就是从你的主线程调用:CWorkerThread * PTHREAD =新CWorkerThread;PTHREAD - GT;个人简历();GUI线程的执行情况
类CGuiThreadImpl使用WTL类CMessageLoop管理一个消息循环。然而,由于CAppModule想了解的过程中所有CMessageLoops,这意味着你必须在构造函数传递到您的CAppModule实例的指针。
为了实现一个GUI线程,从CGuiThreadImpl派生类(可选)重写以下方法:BOOL InitializeThread()来执行线程初始化。举例来说,这是创建窗口的好地方。返回FALSE停止线程。无效CleanupThread(DWORD)来执行清理任务。 DWORD参数是消息循环的退出代码。处理消息
你也可以添加消息映射到你的线程类使用的标准宏,BEGIN_MSG_MAP。然而,这些被称为不是针对一个窗口,即hWnd参数为NULL的消息。
记住,你可以访问的线程的消息循环使用CAppModule:GetMessageLoop(),这样你就可以,例如,安装额外的CMessageFilters。要做到这一点的地方,将InitializeThread和CleanupThread方法。范例
下面的例子显示了一个简单的GUI线程类创建一个计时器和响应WM_TIMER消息#quot;。Thread.hquot;类CTimerThread:公共CGuiThreadImpllt; CTimerThreadgt;{ BEGIN_MSG_MAP(CTimerThread) & #160; MESSAGE_HANDLER(WM_TIMER消息,的OnTimer) END_MSG_MAP()私人: UINT_PTR m_nTimerId;市民: CTimerThread(CAppModule * pModule) CGuiThreadImpllt; CTimerThreadgt;(pModule) {} 布尔InitializeThread() { & #160;m_nTimerId =::SetTimer的(NULL,0,1000,NULL);   ;返回(m_nTimerId = 0); } 无效CleanupThread(DWORD)&# 160; { ::KillTimer(NULL,m_nTimerId); } LRESULT的OnTimer(UINT,WPARAM,LPARAM,BOOLamp;) {  0;::的MessageBeep(MB_ICONASTERISK); 返回0; }};
的"计时器线程"已被创建并停止从主线程:类CMainFrame:... ...{ CTimerThread * m_pTimerThread; / / ... LRESULT的OnCreate(LPCREATESTRUCT) { / / ... m_pTimerThread =新CTimerThread(AMP; _Module);  ; / / ... } 无效OnDestroy() { / / ... g_pTimerThread - GT;调用PostQuitMessage();  60; g_pTimerThread - GT的join(); 删除g_pTimerThread;&# 160; / / ... }};使用类
所有的线程类都包含在一个头文件,Thread.h,您可以使用在文章开始给出的链接下载。为了方便的类,你只需要在您的项目Thread.h。
如果你想在ATL的但并非WTL的项目中使用的类,您将需要从代码中删除所有的GUI线程相关的章节。其他类(CThreadT和CThreadImpl)的工作以及与"纯"的ATL。结论
使用本文中介绍的类的集合,它是有可能实现一个更清洁,更面向对象的多线程应用程序的设计。其他ATL / WTL的类中发现一个类似模板的设计使得它易于理解和整合。修订历史2006年6月30日原来的文章。2006年6月31日CThreadT:现在调用_beginthreadex(上_ATL_MIN_CRT)如果可能的话。添加方法CThreadT::退出。

回答

评论会员:barto 时间:2011/12/06
文章感谢
NBSP
但是存在的一个大的警告有关的构造函数:。
问题是没有,构造函数是在创作者的线程运行,但该CThreadImpllt ; GT的构造函数调用:的CreateThread(立即启动该线程)派生类的构造更是被称为前。所以运行()被称为没有就算完成了构造,造成损坏的类成员

我目前正在评估这个问题,如果有一个通用的解决方案。 (除明显,这是CREATE_SUSPENDED和恢复)
评论会员:蒂尔Krullmann 时间:2011/12/06
您好barto,

感谢您的答复,这是一个好点。在一段时间,我没有使用此代码,但我认为这是我为什么不使用构造函数来初始化线程的数据。相反,我写了一个Initialize()方法,是从线程PROC调用,如代码示例中的。也许我应该在文章中强调这一点
评论会员:。David_LoveCpp 时间:2011/12/06
更新时间:2007年11月

这个类创建一个工作线程或使用一个现有的,等待一个或多个内核对象的句柄,并执行一个指定的客户端功能时,其中一个手柄信号。


模板
类CWorkerThread


参数
ThreadTraits
提供的线程创建函数的类,如CRTThreadTraits或Win32ThreadTraits
:直到Krullmann
评论会员:游客 时间:2011/12/06
喜大卫,我不知道,如果CWorkerThread有回,当我写这篇文章。我觉得我用VisualStudio2003中的项目,从我画的代码(和当时的ATL版本)。当然,我鼓励大家使用或诉诸第三方库或"重新发明轮子之前"扩展平台提供什么。然而,在CWorkerThread寻找时,我注意到,其重点是在后台工作线程和COM的,而我主要需要的是添加消息映射到线程在非COMWTL的应用程序使用标准宏。我认为CThreadImpl和CGuiThreadImpl之间的分离,后来才创造一个更清洁的设计
会员926437 |错误C2488:"_ThreadProcThunk":"裸体"只能适用于函数的定义
评论会员:游客 时间:2011/12/06
rafranco:|嗨,首先,我想感谢您对这篇文章。非常好的工作。我工作的一个线程的进度对话框显示时,我的应用程序会做一些长时间工作。你有没有这样的任何样品吗?我发现了一个关于线程的进度对话框的代码项目的文章,但它不能是MFCWTL的。你能帮助我吗?感谢拉斐尔佛朗哥rafaelfc1208@hotmail.com
蒂尔Krullmann
评论会员:游客 时间:2011/12/06
您好拉斐尔,你真的想实​​际的对话将在它自己的线程?这听起来像你应该做的"长时间工作"在一个非GUI的辅助线程,和后经常"改变"事件在主界面线程的窗口的进展。它的方便使用PBM_SETPOS消息,所以你可以发送您的活动,直接向一个进度条,或者其他一些窗口,依次更新进度条。我已经做了类似与CWorkerThread接受通用CTask对象的类。下面是一些摘录。从OpenInventor项目,所以我用它的一些结构,但也许你得到的点。codeprespanclass="code-keyword"void/spanCWorkerThread::SetProgressBar(HWNDhwndProgress){ ::InterlockedExchangePointer((void**)&m_wndProgress.m_hWnd,(void*)hwndProgress);  spanclass="code-keyword"if/span(m_wndProgress.IsWindow()) m_wndProgress.GetRange(&m_ProgressRange);} BOOLCWorkerThread::SetProgressMarquee(BOOLbMarquee){ spanclass="code-keyword"if/span(m_wndProgress.IsWindow()) {spanclass="code-preprocessor"#if/span(_WIN32_WINNT=0x0501) m_wndProgress.ModifyStyle(bMarquee?spanclass="code-digit"0/span:PBS_MARQUEE,bMarquee?PBS_MARQUEE:spanclass="code-digit"0/span); spanclass="code-keyword"return/spanm_wndProgress.PostMessage(PBM_SETMARQUEE,(WPARAM)bMarquee, (LPARAM)ASYNC_PROGRESS_MARQUEE_UPDATE_TIME); spanclass="code-preprocessor"#endif/span } spanclass="code-keyword"return/spanFALSE;} spanclass="code-keyword"void/spanCWorkerThread::UpdateProgress(spanclass="code-keyword"float/spanfProgress){ spanclass="code-keyword"if/span(m_wndProgress.IsWindow()) { ATLASSERT(m_ProgressRange.iHighm_ProgressRange.iLow); ATLASSERT(fProgress=spanclass="code-digit"0/span.0f&&fProgress<=spanclass="code-digit"1/span.0f);  spanclass="code-keyword"int/spannRange=m_ProgressRange.iHigh-m_ProgressRange.iLow; spanclass="code-keyword"int/spannProgress=m_ProgressRange.iLow+(spanclass="code-keyword"int/span)(fProgress*nRange);  m_wndProgress.PostMessage(PBM_SETPOS,(WPARAM)nProgress,0L); }} spanclass="code-keyword"void/spanCWorkerThread::OnProgressChanged(CWorkerThread*pEvaluator,SoSensor*pSensor){ SoField*pField=((SoFieldSensor*)pSensor)-getAttachedField();  ATLASSERT(pEvaluator!=NULL); ATLASSERT(pField!=NULL); ATLASSERT(pField-isOfType(SoSFFloat::getClassTypeId()));  pEvaluator-UpdateProgress(((SoSFFloat*)pField)-getValue());}/pre/code
萨蒂赵
评论会员:游客 时间:2011/12/06
我CGuiThreadImpl帮助类和定时器的例子WIN2003和VC2005写一个测试程序,发现死锁undefinitely我创建了一个窗口,其中调用你的榜样,立即窗口被摧毁(连续调用PostQuitMessage(),加入(在CMainFrame):OnDestroy)我发现,当调用PostQuitMessage执行计时器线程可能尚未步入它的消息循环。WM_QUIT消息是不会发布到线程的消息循环,所以WaitForSingleObject的不会返回永远和应用程序死锁。我希望你能得到什么,我说。
VedicAnand
评论会员:游客 时间:2011/12/06
我也发现了相同的。尽管它可以得到很好的照顾,但应考虑前waitfunctions。我ondestroy实际上完成关闭主应用程序,但进程中运行的进程表显示,我想它可以在僵尸状态
。JBurkey
评论会员:游客 时间:2011/12/06
只是一个建议,但为什么不能为模板参数线程公寓?不是一个大的事情,但它使主线程函数取出另一块正交码只是一点点清洁。尼斯的工作,虽然。这是很好的学生使用WTL的。我采访了一堆家伙来学校那些认为MFC是一个适用于所有的花花公子框架,它需要从字面上年打破他们的习惯。J
olegxxx
评论会员:游客 时间:2011/12/06
您已经满足好学生,但是我的经验表明,他们认为,净是一个适用于所有的花花公子框架。。
yafan
评论会员:游客 时间:2011/12/06
只是想知道你为什么不使用_beginthread,而不是因为_beginthread的CreateThreadintializesCRT正确,所以文档断言。,-Y
维迪奇Trifunovic
评论会员:游客 时间:2011/12/06
yafan写道:只是想知道你为什么不使用_beginthread,而不是因为_beginthread的CreateThreadintializesCRT正确,所以文档断言。大概是因为WTL的往往是没有显像管。但我同意,应该检查ATL_MIN_CRT宏和使用的CreateThread,只有当它被定义,否则_beginthreadex(不_beginthread{S0})我同意-很好的文章。从我5。imgsrc=http://www.orcode.com/upimg/2011_12_06_15_17_00_0.gif。如果你发现有用的东西在这里,请让我知道删除它
。直到Krullmann
评论会员:游客 时间:2011/12/06
感谢您的反馈意见很多。我通常很难使任何CRT调用WTL的项目,因此,使用_beginthreadex没来我的脑海。但我想这不能伤害...当然,现在ExitThread和_endthreadex之间的区别也成为必要的,所以我增加了一个新的退出方法(见文章),这当然只能从内运行()但是,我不知道,如果所有的线程函数工作在CRT上线程处理漠然;文档中提到的ResumeThread,这意味着SuspendThread也将工作。我想大部分的功能​​只是读取或修改线程控制块的句柄指向的某些部分,所以应该没有什么不同。但对于TerminateThread?有没有CRT相当于
维迪奇Trifunovic
评论会员:游客 时间:2011/12/06
直到Krullmann写道:我不知道如果所有的线程函数工作的CRT线程句柄漠然不要担心。_beginthreadex返回一个好的OL"处理,您可以使用它,你会使用的CreateThread中(当然,除非你将要使用_endthreadex,而不是ExitThread来避免从CRT内存泄漏)。至于TerminateThread,它通常是一个坏主意,CRT或没有。你为什么需要它?imgsrc=http://www.orcode.com/upimg/2011_12_06_15_17_00_0.gif。如果你发现有用的东西在这里,请让我知道删除它
。直到Krullmann
评论会员:游客 时间:2011/12/06
维迪奇Trifunovic写道:TerminateThread,它通常是一个坏主意,CRT或。你为什么需要它?当然,这是一个坏主意,我从来没有使用它......我什至不使用SuspendThread和ResumeThread。我希望建立一些扭曲的同步结构,采取年龄调试imgsrc=http://www.orcode.com/upimg/2011_12_06_15_17_00_1.gif但是,这段代码的目的是作为一个库,并有可能成为人们偶尔想终止线程,发现这个功能很有用。
巴勃罗Aliskevicius
评论会员:游客 时间:2011/12/06
确实是好东西......一件事,我错过了一个"经理"线程发送命令到一个非UI的辅助线程的一种方式。最近,我一直在使用非UI线程PostThreadMessage,用一个指针为LPARAM,WM_COMMAND和WM_QUIT停止线程,这是一个非阻塞的方式做到这一点。一个几年前,我用来创建为每个工作线程的事件;设置"命令"事件时,线程将执行一个命令,"做"事件设置时,该线程将退出其主要循环。我见过的某处线程队列中,这是由管理线程锁定的同时,也会一个工作者线程的命令时弹出。一大堆锁定布莱恩"...在任何情况下,重写SendCommand(LPVOID)(或更好,SendCommand(T:argument_type*)函数将一个很好的补充。更不用说,你得到了我的5。Pablo_A