您的位置:首页 > 编程语言 > C语言/C++

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

2012-04-20 23:56 435 查看
对于 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.cnblogs.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

<EOF>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: