不使用_beginthread并不会导致内存泄漏

RLib 2016-08-15 01:55:55
使用/MT, /MTd编译, 测试环境vs2015 update3.
线程使用RtlCreateUserThread创建, 通过跟踪输出发现, 并没有如网传那样, 在不使用_beginthread/ex创建的线程中使用部分CRT函数会导致内存泄漏, 或许是高版本CRT修复了这个问题, 总之没有复现.

CRT 运行时初始化的大概流程, 首先分配fls(纤程局部存储), fls每个模块各自都会和只会初始化一次, 由加载该模块的线程在DLLMain/WinMain/main前完成, fls存储PTD(per thread data)结构指针, PTD的初始化有三种途径, 一种是在__acrt_initialize_ptd中分配完fls时(也就是模块加载时), 另一种是进程中加载了CRT支持的DLL(即DLL_THREAD_ATTACH时), 最后一种则是线程第一次调用CRT函数(内部调用__acrt_getptd/_noexit)时, 这些保证了不管线程何时创建都能正确使用CRT(包括signal, 这点尚存疑问, 但通过源码来看并无失败的道理).

而PTD的销毁则是在FlsAlloc时指定的回调destroy_fls中的被指定的, 在线程ExitThread之后会被触发, 从而并不存在内存泄漏问题.

FlsAlloc 5 Main Thread
construct 0x0000029d735d0680 {_pxcptacttab=0x00007ffcd149a360 {RLib_x64d.dll!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} Main Thread
FlsAlloc 7 Main Thread
construct 0x0000029d735d6810 {_pxcptacttab=0x00007ff71e8cac50 {crt_x64d.exe!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} Main Thread
construct 0x0000029d735dbb80 {_pxcptacttab=0x00007ffcd149a360 {RLib_x64d.dll!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} RLib_x64d.dll!System::Threading::Thread::__threadProcWrapper()
construct 0x0000029d735dd770 {_pxcptacttab=0x00007ff71e8cac50 {crt_x64d.exe!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} RLib_x64d.dll!System::Threading::Thread::__threadProcWrapper()
destroy 0x0000029d735dbb80 {_pxcptacttab=0x00007ffcd149a360 {RLib_x64d.dll!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} RLib_x64d.dll!System::Threading::Thread::__threadProcWrapper()
destroy 0x0000029d735dd770 {_pxcptacttab=0x00007ff71e8cac50 {crt_x64d.exe!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} RLib_x64d.dll!System::Threading::Thread::__threadProcWrapper()
The thread 0x13fc has exited with code 0 (0x0).
destroy 0x0000029d735d6810 {_pxcptacttab=0x00007ff71e8cac50 {crt_x64d.exe!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} Main Thread
FlsFree 7 Main Thread
destroy 0x0000029d735d0680 {_pxcptacttab=0x00007ffcd149a360 {RLib_x64d.dll!const __crt_signal_action_t __acrt_exception_action_table[1]} {...} ...} Main Thread
FlsFree 5 Main Thread
The thread 0x720 has exited with code 0 (0x0).
The program '[680] crt_x64d.exe' has exited with code 0 (0x0).

...全文
553 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 12 楼 rrrfff 的回复:
当然了, 如果你指的是跨线程使用strtok, 那我觉得这本身就是错误的设计, 不能归咎于库本身了, 就算_beginthread也没办法解决这样的用法
标准中线程也是 C11 和 C++11 之后才引入的。也就是说之前在C\C++中使用多线程是的未定义行为。 标准引入线程之后也是增加了对应的创建函数,要想得到明确行为的结果,那么就得使用标准规定的方法。
RLib 2016-08-16
  • 打赏
  • 举报
回复
当然了, 如果你指的是跨线程使用strtok, 那我觉得这本身就是错误的设计, 不能归咎于库本身了, 就算_beginthread也没办法解决这样的用法
RLib 2016-08-16
  • 打赏
  • 举报
回复
引用 10 楼 akirya 的回复:
但是用某些crt函数的话,就会出现问题。
比如strtok这种,需要用到全局变量。这个全局变量是线程相关的,又不能是和其他线程的混淆。
并且CreateThread之类的API自然不会做这种事情,这个时候怎么保证正确。


既然发帖了, 这些问题还是澄清下吧.
通过阅读vs提供的crt源码发现, 实际上现在库一方面提供了strtok_s这样的线程安全版本, 另一方面strtok也不再引用线程共享的全局变量, 而是通过__acrt_getptd()->_strtok_token得到线程独占的context结构, 所以并无任何问题, 同样保证正确.
  • 打赏
  • 举报
回复
引用 8 楼 rrrfff 的回复:
[quote=引用 7 楼 的回复:] 至于使用其他创建线程,不保证正确。
使用_beginthread自然是正确的没错, 但是不使用, 就目前的ucrt源码来看, 他依然保证正确, 并且我平时的使用过程中也并未发现问题, 当然也可能会有遗漏. 那么一个结论是, 不必过于纠结是否使用_beginthread, 比如有时候使用更底层的api可以获得更大的灵活性.[/quote] 如果是全部api自然没问题,其他语言使用api都这么用过来了。 但是用某些crt函数的话,就会出现问题。 比如strtok这种,需要用到全局变量。这个全局变量是线程相关的,又不能是和其他线程的混淆。 并且CreateThread之类的API自然不会做这种事情,这个时候怎么保证正确。
RLib 2016-08-16
  • 打赏
  • 举报
回复
引用 5 楼 chengbar 的回复:
首先分配fls(纤程局部存储) ,这是哪里的资料? 线程局部存储不是 tls么?
线程局部存储是tls没错, 但目前ms crt用的纤程局部存储
RLib 2016-08-16
  • 打赏
  • 举报
回复
引用 7 楼 的回复:
至于使用其他创建线程,不保证正确。
使用_beginthread自然是正确的没错, 但是不使用, 就目前的ucrt源码来看, 他依然保证正确, 并且我平时的使用过程中也并未发现问题, 当然也可能会有遗漏. 那么一个结论是, 不必过于纠结是否使用_beginthread, 比如有时候使用更底层的api可以获得更大的灵活性.
  • 打赏
  • 举报
回复
引用 4 楼 rrrfff 的回复:
[quote=引用 3 楼 akirya 的回复:] 不会导致内存泄漏吧,问题在于某些内部使用全局变量的函数不正常。
咦, 来的都是大佬... 嗯, 内存泄漏未能复现, 不过很多参考书和网上各种转载都宣称存在内存泄漏, 或许是基于老式CRT的观点.[/quote] 使用_beginthread _beginthreadex 是正确的, 至于使用其他创建线程,不保证正确。
「已注销」 2016-08-15
  • 打赏
  • 举报
回复
C++17已经支持多线程了,这个老古懂还是凉起来吧
sevancheng 2016-08-15
  • 打赏
  • 举报
回复
首先分配fls(纤程局部存储) ,这是哪里的资料? 线程局部存储不是 tls么?
RLib 2016-08-15
  • 打赏
  • 举报
回复
引用 3 楼 akirya 的回复:
不会导致内存泄漏吧,问题在于某些内部使用全局变量的函数不正常。
咦, 来的都是大佬... 嗯, 内存泄漏未能复现, 不过很多参考书和网上各种转载都宣称存在内存泄漏, 或许是基于老式CRT的观点.
  • 打赏
  • 举报
回复
不会导致内存泄漏吧,问题在于某些内部使用全局变量的函数不正常。
Eleven 2016-08-15
  • 打赏
  • 举报
回复
赵4老师 2016-08-15
  • 打赏
  • 举报
回复

1,649

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 非技术类
社区管理员
  • 非技术类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧