设为首页 收藏本站
查看: 940|回复: 0

在 Windows 7 下如何使用 native c++ 正确创建线程

[复制链接]

尚未签到

发表于 2015-5-12 11:02:13 | 显示全部楼层 |阅读模式
  
对于 Windows 7 上的 C++ 程序员来说,创建一个 thread 有以下五种选择。注意,本文最后一个提到的 AfxBeginThread 是用于创建一个 UI 线程,可等同于用于创建 worker thread 的倒数第二个 AfxBeginThread,这里不讨论它。



HANDLE WINAPI CreateThread(
__in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in       SIZE_T dwStackSize,
__in       LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt   LPVOID lpParameter,
__in       DWORD dwCreationFlags,
__out_opt  LPDWORD lpThreadId
);
uintptr_t _beginthread(
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);

看到这四个函数,一个很自然的问题就是 - 那我到底该用哪一个呢?答案是,搞清楚它们之间的区别后,该用哪个取决于具体应用场景。这里浅谈一下它们之间的区别。
CreateThread 是 Windows 的API 函数,提供 OS 级别的创建 thread 的操作,且仅限于 worker thread。主函数中应当避免使用 MFC 和 CRT 的函数。这是因为在它的层面不会做下面两件事:


  • CRT 需要对多线程进行记录和初始化,以保证 C 函数库工作正常,比如一个典型的例子是 strtok。
  • MFC 也需要对多线程进行记录和初始化,以保证 MFC 函数库工作正常。  

_beginthread 和 _beginthreadex 是由 multithreaded 版本的 CRT 提供的,header 是 afxwin.h。创建 thread,其实也是通过 Windows API CreateThread 来实现的。结束 thread,可在主函数(准确的说,是线程 run 到的所有代码,下同)内通过显式调用 _endthread( ) or _endthreadex(unsigned retval) 。注意,当线程从主函数返回后,二者自动会被调用。显式调用二者,可以帮助确保为该 thread 分配的 resource 被恰当恢复,最典型的比如 _tiddata 内存块(/* Structure for each thread's data */),阅读代码便知,_beginthreadex/_beginthreadex  负责分配 _tiddata 内存块,_endthread/_endthreadex 负责释放 _tiddata 内存块。另外还有一点要特别注意,_endthread( ) 会自动 close thread handle, _endthreadex 不会。因此,如果是用 _beginthreadex 创建的 thread,一定要通过调用 CloseHandle API 来手工 close thread handle。

再举个例子,如果使用 CreateThread 创建 thread,但 thread 的主函数却调用了需要读取 _tiddata 的 CRT 函数,CRT 就会为该线程分配一个 _tiddata 内存块。问题来了,CreateThread 创建的 thread,结束时并没有调用 _endthreadex,内存泄漏就这样悄无声息的发生了。我想这就是为什么 MSDN 上会有下面这段话的原因:



A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.[1]
AfxBeginThread 是由 MFC 提供,header 是 afxwin.h。要 end thread,可在主函数内调 AfxEndThread 或直接从 thread 主函数返回。注意不要在一个 MFC 程序中使用 _beginthreadex 或者 CreateThread 来创建 thread。通过阅读该函数的源码不难理解原因。它首先创建了相应的 CWinThread 对象,然后调用 CWinThread::CreateThread 来创建 thread。而 CWinThread::CreateThread 函数一开始就做了很多 MFC 特有的 thread 初始化的操作,比如创建了两个 event 内核对象;然后通过 _beginthreadex 创建 thread。如果在 MFC 程序中,用 MFC 来创建 thread,又用 CRT 或者 Windows API 对 thread,就会出现控制混乱的局面,从而导致一些无法预料的错误。
总结一下:对于 thread 的主函数,如果会调用 CRT 的函数,则必须确保会成对使用 _beginthreadex/_endthreadex; 如果会调用 MFC 的函数,则必须确保会成对使用 AfxBeginThread/ AfxEndThread;如果都没有,才可以使用 CreateThread/ExitThread。一个大致的从顶层到底层的逻辑包含关系是,AfxBeginThread 包含 _beginthreadex; _beginthread 是 _beginthreadex 的功能子集(详细区别在 threadex.c 文件中),但二者都是 CRT 层面;_beginthread 和 _beginthreadex 又包含 CreateThread。
  References:

  [1] http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx , CreateThread reference
  [2] http://www.iyunv.com/yuaqua/archive/2011/11/18/2253492.html , CreateThread, AfxBeginThread, _beginthread, _beginthreadex
  [3] http://msdn.microsoft.com/en-us/library/s3w9x78e%28v=vs.100%29.aspx , AfxBeginThread reference.
  [4] http://msdn.microsoft.com/en-us/library/984x0h58%28v=vs.80%29.aspx , __cdecl, __stdcall, __fastcall
  [5] http://msdn.microsoft.com/en-us/library/kdzttdcb%28v=vs.100%29.aspx , _beginthread vs _beginthreadex

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-66197-1-1.html 上篇帖子: Windows 7 IIS安装 下篇帖子: Windows Phone 7 学习心得(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表