登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

[win32] 线程内存泄露的主要原因  

2008-10-04 18:21:56|  分类: win32 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

最近在看梁总的《编程高手箴言》,其中 win32 线程一段的叙述,解决了我一直以来的一个困惑。记之:

很多参考书上,都说不要用 CreateThread 创建线程、并用 CloseHandle 来关闭这个线程,因为这样会导致内存泄露,而应该用 _beginthread 来创建线程,_endthread 来销毁线程。其实,真正的原因并非如此。

CreateThread 后,线程终止运行后,线程对象仍然在系统中,必须通过 CloseHandle 函数来关闭该线程对象。为什么会引起内存泄露呢?因为当线程的函数用到了C的标准库的时候,很容易导致冲突,所以在创建VC的工程时,系统提示是用单线程还是用多线程的库,因为在C的内部有很多的全局变量。例如 errno。

因为在C的库中有全局变量,如果程序中使用了标准的C的库时,很容易导致运行不正常。所以,微软和Borland都对C的库进行了一些改进。但是这个改进的一个条件就是,如果一个线程已经开始创建,就应该创建一个结构来包含这些全局变量,接着把这些全局变量放入线程的上下文中。从而全局变量就会依赖于这个线程,不会引起冲突。

这样做就会有一个问题,什么时候这个线程开始创建呢?标准的C库是不知道的,而 CreateThread 是操作系统的API。所以需要使用 _beginthread/_endthread 来创建/结束线程,让标准C库为线程化做些准备工作。

当用 _beginthread 来创建,而用 CloseHandle 来关闭线程时,标准C库复制的全局变量就不会被释放,这才是前面说的内存泄露的原因。

另一方面,如果用 CreateThread/CloseHandle 来创建/结束线程,则不要使用标准C库的任何函数。还有一个需要注意的,就是在线程执行完之前,不要使用 CloseHandle 来结束线程,否则也会有异常。我们一般通过 WaitForSingleObject/WaitForMultipleObjects 来等待线程结束。


2008-10-19  多谢 amtf 同学提醒

上面说到 "CloseHandle 来结束线程"有误。主线程强制关闭一个子线程,应该先 TerminateThread 然后 CloseHandle。根据 MSDN 所说:"Closing a thread handle does not terminate the associated thread or remove the thread object. To remove a thread object, you must terminate the thread, then close all handles to the thread."

按《核心编程》中所言,CreateThread 创建的 thread_kernel_object.refcount = 2,一个是系统对其的 ref,一个是 thread 本身对其的 ref。当 ExitThread 后,ref - 1,然后 CloseHandle,ref - 1,此时,kernel object 被释放。

《核心》中还坦言,尽量不要使用 _beginthread/_endthread 而是换用对应的 -ex 版本,因此 _endthread 会同时调用 ExitThread/CloseHandle,因此会有下面的问题:

DWORD dwExitCode;

HANDLE hThread = _beginthread(...);

GetExitCodeThread(hThread, &dwExitCode);

CloseHandle(hThread);

如果在 GetExitCodeThread 之前,线程已经执行结束,则 hThread 就已经失效了,因为 _endthread 里面调用了CloseHandle。而 _endthreadex 只会 ExitThread,并不 CloseHandle。

  评论这张
 
阅读(1587)| 评论(1)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018