笑看风云

记录生活中的启迪与感动
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

为何要用_beginthreadex()而非CreateThread?

Posted on 2010-06-13 16:15  清晨的风  阅读(807)  评论(0)    收藏  举报

转自http://www.cppblog.com/bidepan2023/archive/2007/10/31/35627.html

当你打算实现一个多线程(非MFC)程序,你会选择一个单线程的CRT(C运行时库)吗?如果你的回答是NO, 那么会带来另外一个问题,你选择了CreateThread来创建一个线程吗? 大多数人也许会立刻回答YES。可是很不幸,这是错误的选择。
我先来说一下我的结论,待会告诉你为什么。

如果要作多线程(非MFC)程序,在主线程以外的任何线程内
- 使用malloc(),free(),new
- 调用stdio.h或io.h,包括fopen(),open(),getchar(),write(),printf(),errno
- 使用浮点变量和浮点运算函数
- 调用那些使用静态缓冲区的函数如: asctime(),strtok(),rand()等。
你就应该使用多线程的CRT并配合_beginthreadex(该函数只存在于多线程CRT), 其他情况下你可以使用单线程的CRT并配合CreateThread。

因为对产生的线程而言,_beginthreadex比之CreateThread会为上述操作多做额外的簿记工作,比如帮助strtok()为每个线程准备一份缓冲区。
然而多线程程序极少情况不使用上述那些函数(比如内存分配或者io),所以与其每次都要思考是要使用_beginthreadex还是CreateThread,不如就一棍子敲定_beginthreadex。

当然你也许会借助win32来处理内存分配和Io,这时候你确实可以以单线程crt配合CreateThread,因为io的重任已经从crt转交给了win32。这时通常你应该使用HeapAlloc,HeapFree来处理内存分配,用CreateFile或者GetStdHandle来处理Io。

还有一点比较重要的是_beginthreadex传回的虽然是个unsigned long,其实是个线程Handle(事实上_beginthreadex在内部就是调用了CreateThread),所以你应该用CloseHandle来结束他。千万不要使用ExitThread()来退出_beginthreadex创建的线程,那样会丧失释放簿记数据的机会,应该使用_endthreadex.

//   create/destroy 
BOOL   CXThread::create   (   void   *   pThreadData   ) 
{ 
//   get   ptr   to   data 
m_pThreadData   =   pThreadData; 

//   create   events 
if   (   !createThreadEvents()   ) 
return   FALSE; 

//   create   thread 
#if   defined(UseWin32Thread) 
m_hThread   =   
CreateThread(NULL,0,   CXThread::threadProc,   (LPVOID)   this,   0,   &m_idThread   ); 
#else 
m_hThread   =   (HANDLE) 
_beginthreadex(   NULL,   0,   CXThread::threadProc,   (LPVOID)   this,   0, 
                                        (unsigned   int   *)   &m_idThread   ); 
#endif 

if   (   m_hThread   ==   0   ) 
return   FALSE; 

//   success   so   start   thread 
//::ResumeThread(   m_hThread   ); 
return   TRUE; 
} 


//   thread   proc 
#if   defined(UseWin32Thread) 
DWORD   WINAPI CXThread::threadProc   (   LPVOID   parameter   ) 
#else 
unsigned   _stdcall   CXThread::threadProc   (   LPVOID   parameter   ) 
#endif 
{ 
if   (   !parameter   ) 
return   XTHREAD_NORMAL; 

//   start   thread 
CXThread   *   pThread   =   (CXThread   *)   parameter; 
int   ret   =   pThread-> run(); 

//   exit   the   thread 
#if   defined(UseWin32Thread) 
ExitThread(   XTHREAD_NORMAL); 
#else 
_endthreadex(   XTHREAD_NORMAL); 
#endif 

return   ret; 
} 

< <Windows核心编程> > 中有很详细地介绍。

_beginthreadex是微软的C/C++运行时库函数,CreateThread是操作系统的函数。
_beginthreadex通过调用CreateThread来实现的,但比CreateThread多做了许多工作。
注意:若要创建一个新线程,绝对不要使用CreateThread,而应使用_beginthreadex.
Why?考虑标准C运行时库的一些变量和函数,如errno,这是一个全局变量。全局变量用于
多线程会出什么事,你一定知道的了。故必须存在一种机制,使得每个线程能够引用它自己的
errno变量,又不触及另一线程的errno变量._beginthreadex就为每个线程分配自己的
tiddata内存结构。该结构保存了许多像errno这样的变量和函数的值、地址(自己看去吧)。
通过线程局部存储将tiddata与线程联系起来。具体实现在Threadex.c中有。
结束线程使用函数_endthreadex函数,释放掉线程的tiddata数据块。