_beginthread 和 createthread 区别

上传人:wt****50 文档编号:39573587 上传时间:2018-05-17 格式:DOCX 页数:4 大小:16.64KB
返回 下载 相关 举报
_beginthread 和 createthread 区别_第1页
第1页 / 共4页
_beginthread 和 createthread 区别_第2页
第2页 / 共4页
_beginthread 和 createthread 区别_第3页
第3页 / 共4页
_beginthread 和 createthread 区别_第4页
第4页 / 共4页
亲,该文档总共4页,全部预览完了,如果喜欢就下载吧!
资源描述

《_beginthread 和 createthread 区别》由会员分享,可在线阅读,更多相关《_beginthread 和 createthread 区别(4页珍藏版)》请在金锄头文库上搜索。

1、摘录时间: 2010-04-0518:29 _beginthread 和 CreateThread 区别 收藏 程序员对于 Windows 程序中应该用_beginthread 还是 CreateThread 来创建线程,一直有所 争论。本文将从对 CRT 源代码出发探讨这个问题。 I. 起因 今天一个朋友问我程序中究竟应该使用_beginthread 还是 CreateThread,并且告诉我如果使 用不当可能会有内存泄漏。其实我过去对这个问题也是一知半解,为了对朋友负责,专门 翻阅了一下 VC 的运行库(CRT)源代码,终于找到了答案。 II. CRT CRT(C/C+ Runtime L

2、ibrary)是支持 C/C+运行的一系列函数和代码的总称。虽然没有一个很 精确的定义,但是可以知道,你的 main 就是它负责调用的,你平时调用的诸如 strlen、strtok、time、atoi 之类的函数也是它提供的。我们以 Microsoft Visual.NET 2003 中 所附带的 CRT 为例。假设你的.NET 2003 安装在 C:Program FilesMicrosoft Visual Studio .NET 2003 中,那么 CRT 的源代码就在 C:Program FilesMicrosoft Visual Studio .NET 2003Vc7crtsrc 中。

3、既然有了这些实现的源代码,我们就可以找到一切解释了。 III. _beginthread/_endthread 这个函数究竟做了什么呢?它的代码在 thread.c 中。阅读代码,可以看到它最终也是通过 CreateThread 来创建线程的,主要区别在于,它先分配了一个_tiddata,并且调用了_initptd 来初始化这个分配了的指针。而这个指针最后会被传递到 CRT 的线程包装函数_threadstart 中,在那里会把这个指针作为一个 TLS(Thread Local Storage)保存起来。然后_threadstart 会调用我们传入的线程函数,并且在那个函数退出后调用_endt

4、hread。这里也可以看到, _threadstart 用一个_try/_except 块把我们的函数包了起来,并且在发生异常的时候,调 用 exit 退出。 (_threadstart 和 endthread 的代码都在 thread.c 中) 这个_tiddata 是一个什么样的结构呢?它在 mtdll.h 中定义,它的成员被很多 CRT 函数所用 到,譬如 int _terrno,这是这个线程中的错误标志;char* _token,strtok 以来这个变量记 录跨函数调用的信息,.。 那么_endthread 又做了些什么呢?除了调用浮点的清除代码以外,它还调用了_freeptd 来

5、释放和这个线程相关的 tiddata。也就是说,在 _beginthread 里面分配的这块内存,以及在 线程运行过程中其它 CRT 函数中分配并且记录在这个内存结构中的内存,在这里被释放了。通过上面的代码,我们可以看到,如果我使用_beginthread 函数创建了线程,它会为我创 建好 CRT 函数需要的一切,并且最后无需心,就可以把清除工作做得很好,可能唯一 需要注意的就是,如果需要提前终止线程,最好是调用_endthread 或者是返回,而不要调 用 ExitThread,因为这可能造成内存释放不完全。同时我们也可以看出,如果我们用 CreateThread 函数创建了线程,并且不对

6、C 运行库进行调用(包括任何间接调用) ,就不必 担心什么问题了。 IV. CreateThread 和 CRT 或许有人会说,我用 CreateThread 创建线程以后,我也调用了 C 运行库函数,并且也使用 ExitThread 退出了,可是我的程序运行得好好的,既没有因为 CRT 没有初始化而崩溃,也 没有因为忘记调用 _endthread 而发生内存泄漏,这是为什么呢,让我们继续我们的 CRT 之旅。 假设我用 CreateThread 创建了一个线程,我调用 strtok 函数来进行字符串处理,这个函数 肯定是需要某些额外的运行时支持的。strtok 的源代码在 strtok.c

7、中。从代码可见,在多线 程情况下,strtok 的第一句有效代码就是_ptiddata ptd = _getptd(),它通过这个来获得当前 的 ptd。可是我们并没有通过_beginthread 来创建 ptd,那么一定是_getptd 捣鬼了。打开 tidtable.c,可以看到_getptd 的实现,果然,它先尝试获得当前的 ptd,如果不能,就重新 创建一个,因此,后续的 CRT 调用就安全了。可是这块 ptd 最终又是谁释放的呢?打开 dllcrt0.c,可以看到一个 DllMain 函数。在 VC 中,CRT 既可以作为一个动态链接库和主程序 链接,也可以作为一个静态库和主程序链接

8、,这个在 Project Setting-Code Generations 里 面可以选。当 CRT 作为 DLL 链接到主程序时,DllMain 就是 CRT DLL 的入口。Windows 的 DllMain 可以由四种原因调用:Process Attach/Process Detach/Thread Attach/Thread Detach, 最后一个,也就是当线程函数退出后但是线程还没有销毁前,会在这个线程的上下文中用 Thread Detach 调用 DllMain,这里,CRT 做了一个_freeptd(NULL),也就是说,如果有 ptd, 就 free 掉。所以说,恰巧没有发生

9、内存泄漏是因为你用的是动态链接的 CRT。 于是我们得出了一个更精确的结论,如果我没有使用那些会使用_getptd 的 CRT 函数,使用 CreateThread 就是安全的。 V. 使用 ptd 的函数 那么,究竟那些函数使用了_getptd 呢?很多!在 CRT 目录下搜索_getptd,你会发觉很多 意想不到的函数都用到了它,除了 strtok、rand 这类需要保持状态的,还有所有的字符串 相关函数,因为它们要用到 ptd 中的 locale 信息;所有的 mbcs 函数,因为它们要用到 ptd 中的 mbcs 信息,.。 VI. 测试代码 下面是一段测试代码(leaker 中用到了

10、 atoi,它需要 ptd): #include #include #include #include volatile bool threadStarted = false; void leaker() std:cout atoi( “0“ ) std:endl; DWORD _stdcall CreateThreadFunc( LPVOID ) leaker();threadStarted = false;return 0; DWORD _stdcall CreateThreadFuncWithEndThread( LPVOID ) leaker();threadStarted = fal

11、se;_endthread();return 0; void _cdecl beginThreadFunc( LPVOID ) leaker();threadStarted = false; int main() for(;)while( threadStarted )Sleep( 5 );threadStarted = true; / _beginthread( beginThreadFunc, 0, 0 );/1CreateThread( NULL, 0, CreateThreadFunc, 0, 0, 0 );/2 / CreateThread( NULL, 0, CreateThrea

12、dFuncWithEndThread, 0, 0, 0 );/3return 0; 如果你用 VC 的多线程+静态链接 CRT 选项去编译这个程序,并且尝试打开 1、2、3 之中的 一行,你会发觉只有 2 打开的情况下,程序才会发生内存泄漏(可以在 Task Manager 里面 明显的观察到) 。3 之所以不会出现内存泄漏是因为主动调用了_endthread。 VII. 总结 如果你使用了 DLL 方式链接的 CRT 库,或者你只是一次性创建少量的线程,那么你或许可 以采取鸵鸟策略,忽视这个问题。上面一节代码中第 3 种方法基于对 CRT 库的了解,但是 并不保证这是一个好的方法,因为每一个

13、版本的 VC 的 CRT 可能都会有些改变。看来,除非你的头脑清晰到可以记住这一切,或者你可以不厌其烦的每调用一个 C 函数都查一下 CRT 代码,否则总是使用 _beginthread(或者它的兄弟_beginthreadex)是一个不错的选择。后记 网友 condor 指出本文的一个错误:在 dllcrt0.c 中,DllMain 的 Thread Detach 所释放的 ptd,其实是 dllcrt0.c 的 DllMain 中的 Thread Attach 所创建的。也就是说,当你用 CRT DLL 的时候,DllMain 对线程做了一切初始化/清除工作。我查看源代码,thread.c 中的 _threadstart 函数,在设置 TLS 之前做了检查,这其实就是为了避免重复设置导致的内存泄 漏。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号