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

CreateThread与_beginthreadex的区别

2017-01-03 15:23 204 查看
一  为什么不直接使用CreateThread,而要使用运行库函数_beginthreadex函数?

1  CreateThread不会在创建线程之前申请一个_tiddata结构内存块,当一个线程调用一个需要_tiddata结构的C/C++运行库函数(例如errno, strtok, _gmtime等)时,虽然C/C++运行库函数会为主调函数申请并初始化一个_tiddata块,但是在线程退出的时候,如果不是调用_endthreadex函数的话,_tiddata块将不能被释放,从而导致内存泄露。(当模块链接到C/C++运行库的DLL版本时,这个库会在线程终止时收到一个DLL_THREAD_DETACH通知,然后会执行释放_tiddata块)

2  如果线程使用了C/C++运行库的signal函数(该函数用于设置中断信号处理函数,比如可以使用此函数屏蔽CTRL+C,更多介绍可参阅MSDN),则整个进程会异常退出。因为CreateThread创建的线程其结构化异常处理帧(SEH)没有设置,导致程序不能处理异常。

二  _beginthreadex函数都干了啥

1  对照前面第一点中提到的CreateThread的隐患,_beginthreadex都做了相应的处理,首先_beginthreadex函数会申请_tiddata数据结构块,然后会将要创建线程的入口点和参数都设置到该_tiddata数据结构中。

2  _beginthreadex内部确实调用了CreateThread这一系统API,因为这是操作系统创建新线程的唯一方法。

但是调用CreateThread时,所给的新线程入口点是_threadstartex,而不是外部由程序员提供的线程入口地址,同时,传递给CreateThread的参数也不是外部由程序员提供的参数,而是_threadstartex申请的_tiddata数据结构块。所以,接下来的工作都将转入到_threadstartex函数中。

三  _threadstartex函数做了什么工作

1  首先使用TlsSetVlue函数将_tiddata结构和线程关联起来。

2  初始化浮点操作支持。

3  设置SEH链,捕捉C++异常,例如在第一点中提到的C/C++运行库的signal函数,需要异常处理支持。

4  以上工作就绪后,会调用保存在_tiddata中由外部程序员提供的线程入口函数,并传入保存在_tiddata中的参数,这样就启动了线程代码。

5  该函数将一直等待线程的结束,当线程返回时,调用_endthreadex函数,在_endthreadex内部通过_getptd和_freeptd释放掉_tiddata结构的内存。_endthreadex函数最终会调用系统的ExitThread API(这时候调用该函数已经是安全的了,因为_tiddata内存已经释放)。

四  为什么不应该调用ExitThread函数

这一点在第三点中已经说明了,直接调用ExitThread函数会导致_tiddata内存无法释放,如果不得已,外部必须要结束一个线程,可以调用_endthreadex来结束线程,_endthreadex会释放_tiddata内存块。

五  不应该使用_beginthread、_endthread函数。

_beginthread的参数缺少安全描述符. 而且它是创建线程的时候先以挂起状态创建 (CreateThread会填充ptd->_thandle和ptd->_tid) 然后再ResumeThread

_beginthread是根据传进来的参数创建线程,此外,两者失败返回值不同,ex版本的与Windows API CreateThread返回值是一直的,这也是提倡使用后者的原因之一。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息