关闭

CreateThread与_beginthreadex的区别

标签: CreateThread_beginthreadexc++windows
77人阅读 评论(0) 收藏 举报
分类:

一  为什么不直接使用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返回值是一直的,这也是提倡使用后者的原因之一。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:66386次
    • 积分:2524
    • 等级:
    • 排名:第14594名
    • 原创:195篇
    • 转载:27篇
    • 译文:0篇
    • 评论:1条
    文章分类
    最新评论