正确使用pthread_create,防止内存泄漏

近日,听说pthread_create会造成内存泄漏,觉得不可思议,因此对posix(nptl)的线程创建和销毁进行了分析。

分析结果:如果使用不当,确实会造成内存泄漏。

产生根源:pthread_create默认创建的线程是非detached的。

预防方式:要么创建detached的线程,要么线程线程的start_routine结束之前detached,要么join

分析过程如下:

  1.查看pthread_create源代码,核心代码如下(nptl/pthread_create.c):

点击(此处)折叠或打开

  1. int
  2. __pthread_create_2_1 (newthread, attr, start_routine, arg)
  3. pthread_t *newthread;
  4. const pthread_attr_t *attr;
  5. void *(*start_routine) (void *);
  6. void *arg;
  7. {
  8. STACK_VARIABLES;
  9. const struct pthread_attr *iattr = (struct pthread_attr *) attr;
  10. if (iattr == NULL)
  11. /* Is this the best idea? On NUMA machines this could mean
  12. accessing far-away memory. */
  13. iattr = &default_attr;
  14. struct pthread *pd = NULL;
  15. int err = ALLOCATE_STACK (iattr, &pd);//为tcb分配内存
  16. if (__builtin_expect (err != 0, 0))
  17. /* Something went wrong. Maybe a parameter of the attributes is
  18. invalid or we could not allocate memory. */
  19. return err;
  20. //……
  21. err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);//正式创建线程

2.查看createthread.c(nptl/sysdeps/pthread/createthread.c)

点击(此处)折叠或打开

  1. static int
  2. create_thread (struct pthread *pd, const struct pthread_attr *attr,
  3. STACK_VARIABLES_PARMS)
  4. {
  5. #ifdef TLS_TCB_AT_TP
  6. assert (pd->header.tcb != NULL);
  7. #endif
  8. //……
  9. int res = do_clone (pd, attr, clone_flags, start_thread,
  10. STACK_VARIABLES_ARGS, 1);//clone一个进程

3.接着看start_thread(nptl/pthread_create.c)做了什么

点击(此处)折叠或打开

  1. static int
  2. start_thread (void *arg)
  3. {
  4. struct pthread *pd = (struct pthread *) arg;
  5. //……
  6. /* Run the code the user provided. */
  7. #ifdef CALL_THREAD_FCT
  8. THREAD_SETMEM (pd, result, CALL_THREAD_FCT (pd));
  9. #else
  10. THREAD_SETMEM (pd, result, pd->start_routine (pd->arg)); //正式启动线程的执行,并等待执行完成
  11. #endif
  12. //……
  13. if (IS_DETACHED (pd))
  14. /* Free the TCB.  */
  15. __free_tcb (pd);//如果设置detached标志,则释放tcb占用的内容,否则直接返回
  16. else if (__builtin_expect (pd->cancelhandling & SETXID_BITMASK, 0))
  17. {
  18. /* Some other thread might call any of the setXid functions and expect
  19. us to reply.  In this case wait until we did that.  */
  20. do
  21. lll_futex_wait (&pd->setxid_futex, 0, LLL_PRIVATE);
  22. while (pd->cancelhandling & SETXID_BITMASK);
  23. /* Reset the value so that the stack can be reused.  */
  24. pd->setxid_futex = 0;
  25. }

从上面的过程,我们可以看到,如果在创建线程的时候,如果没有设置detached标志,则tcb内存永远不会释放

接下来,我们看看pthread_detach(npth/pthread_detach.c)做了什么

点击(此处)折叠或打开

  1. int
  2. pthread_detach (th)
  3. pthread_t th;
  4. {
  5. struct pthread *pd = (struct pthread *) th;
  6. /* Make sure the descriptor is valid. */
  7. if (INVALID_NOT_TERMINATED_TD_P (pd))
  8. /* Not a valid thread handle. */
  9. return ESRCH;
  10. int result = 0;
  11. /* Mark the thread as detached. */
  12. if (atomic_compare_and_exchange_bool_acq (&pd->joinid, pd, NULL))
  13. {
  14. /* There are two possibilities here. First, the thread might
  15. already be detached. In this case we return EINVAL.
  16. Otherwise there might already be a waiter. The standard does
  17. not mention what happens in this case. */
  18. if (IS_DETACHED (pd))
  19. result = EINVAL;
  20. }
  21. else
  22. /* Check whether the thread terminated meanwhile. In this case we
  23. will just free the TCB. */
  24. if ((pd->cancelhandling & EXITING_BITMASK) != 0)
  25. /* Note that the code in __free_tcb makes sure each thread
  26. control block is freed only once. */
  27. __free_tcb (pd);//经过一系列的容错判断,直接释放tcb占用的内存
  28. return result;
  29. }

最后,我们看一下pthread_join(nptl/pthread_join.c)做了什么

点击(此处)折叠或打开

  1. int
  2. pthread_join (threadid, thread_return)
  3. pthread_t threadid;
  4. void **thread_return;
  5. {
  6. struct pthread *pd = (struct pthread *) threadid;
  7. /* Make sure the descriptor is valid. */
  8. if (INVALID_NOT_TERMINATED_TD_P (pd))
  9. /* Not a valid thread handle. */
  10. return ESRCH;
  11. /* Is the thread joinable?. */
  12. if (IS_DETACHED (pd))
  13. /* We cannot wait for the thread. */
  14. return EINVAL;
  15. struct pthread *self = THREAD_SELF;
  16. int result = 0;
  17. /* During the wait we change to asynchronous cancellation. If we
  18. are canceled the thread we are waiting for must be marked as
  19. un-wait-ed for again. */
  20. pthread_cleanup_push (cleanup, &pd->joinid);
  21. /* Switch to asynchronous cancellation. */
  22. int oldtype = CANCEL_ASYNC ();
  23. if ((pd == self
  24. || (self->joinid == pd
  25. && (pd->cancelhandling
  26. & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
  27. | TERMINATED_BITMASK)) == 0))
  28. && !CANCEL_ENABLED_AND_CANCELED (self->cancelhandling))
  29. /* This is a deadlock situation. The threads are waiting for each
  30. other to finish. Note that this is a "may" error. To be 100%
  31. sure we catch this error we would have to lock the data
  32. structures but it is not necessary. In the unlikely case that
  33. two threads are really caught in this situation they will
  34. deadlock. It is the programmer‘s problem to figure this
  35. out. */
  36. result = EDEADLK;
  37. /* Wait for the thread to finish. If it is already locked something
  38. is wrong. There can only be one waiter. */
  39. else if (__builtin_expect (atomic_compare_and_exchange_bool_acq (&pd->joinid,
  40. self,
  41. NULL), 0))
  42. /* There is already somebody waiting for the thread. */
  43. result = EINVAL;
  44. else
  45. /* Wait for the child. */
  46. lll_wait_tid (pd->tid);
  47. /* Restore cancellation mode. */
  48. CANCEL_RESET (oldtype);
  49. /* Remove the handler. */
  50. pthread_cleanup_pop (0);
  51. if (__builtin_expect (result == 0, 1))
  52. {
  53. /* We mark the thread as terminated and as joined. */
  54. pd->tid = -1;
  55. /* Store the return value if the caller is interested. */
  56. if (thread_return != NULL)
  57. *thread_return = pd->result;//设置返回值
  58. /* Free the TCB. */
  59. __free_tcb (pd);/释放TCB占用内存
  60. }
  61. return result;
  62. }

综上,如果要保证创建线程之后,确保无内存泄漏,必须采用如下方法来规范pthread_create的使用:

方法一、创建detached的线程

点击(此处)折叠或打开

  1. void run() {
  2. return;
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_attr_t attr;
  7. pthread_attr_init( &attr );
  8. pthread_attr_setdetachstate(&attr,1);
  9. pthread_create(&thread, &attr, run, 0);
  10. //......
  11. return 0;
  12. }

方法二、要么线程线程的start_routine结束之前detached

点击(此处)折叠或打开

  1. void run() {
  2. pthread_detach(pthread_self());
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_create(&thread, NULL, run, 0);
  7. //......
  8. return 0;
  9. }

方法三、主线程使用pthread_join

点击(此处)折叠或打开

  1. void run() {
  2. return;
  3. }
  4. int main(){
  5. pthread_t thread;
  6. pthread_create(&thread, NULL, run, 0);
  7. //......
  8. pthread_join(thread,NULL);
  9. return 0;
  10. }
时间: 2024-08-27 12:36:31

正确使用pthread_create,防止内存泄漏的相关文章

100%正确的内存泄漏分析工具       --------tMemMonitor (TMM)

C/C++由于灵活.高效的优点一直以来都是主流的程序设计语言之一,但是其内存的分配与释放均由程序员自己管理,当由于疏忽或错误造成程序未能释放不再使用的内存时就会造成内存泄漏.在大型.复杂的应用程序中,内存泄漏往往是最常见的问题,因而及时解决内存泄漏非常必要.tMemMonitor (TMM)作为一个专业.准确.易用的内存泄漏分析工具,可以帮助C/C++程序员迅速地解决内存泄漏这个令人头疼的问题. TMM下载地址: 一.开发背景 目前市面上已有一些Windows平台下的内存泄漏动态检测工具,比如U

Android开发 |常见的内存泄漏问题及解决办法

在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要再使用了,本该被回收时,而另外一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了. 内存泄漏有什么影响呢?它是造成应用程序OOM的主要原因之一.由于Android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时

(转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对 C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃 C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能

并发编程(四):ThreadLocal从源码分析总结到内存泄漏

一.目录 1.ThreadLocal是什么?有什么用? 2.ThreadLocal源码简要总结? 3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLocal是什么?有什么用? 引入话题:在并发条件下,如何正确获得共享数据?举例:假设有多个用户需要获取用户信息,一个线程对应一个用户.在mybatis中,session用于操作数据库,那么设置.获取操作分别是session.set().session.get(),如何保证每个线程都能正确操作达到想要的结果? /** * 回顾sync

使用Xcode Instruments Leak解决内存泄漏问题

iOS 5.0之后apple引入了Xcode编译器特性ARC(Automatic Reference Counting,自动引用计数)来帮助开发者管理内存,但为了追求app的高性能与减少安装包大小,工作中很多时候需要我们手动管理内存.再牛的开发者也不能保证自己写的code 100%没有内存泄露,出现内存泄露不可怕,可怕的是我们时间与精力花了大把,但内存泄露依旧没解决,即影响了工作效率也影响自己的心情. 下面就讲解xcode中的内存调试神器---Instruments Leak ,不管是ios开发

非静态内部类创建静态实例造成的内存泄漏

请大家思考,为什么会内存泄漏? 1. 首先,非静态内部类默认会持有外部类的引用. 2. 然后又使用了该非静态内部类创建了一个静态的实例. 3. 该静态实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收. 正确的做法有两种,一种是将内部类testResource改成静态内部类,还有就是将testResource抽取出来,封装成一个单例,如上一个例子那样,但是需要context时单例要切记注意Context的泄漏,使用ap

线程造成的内存泄漏

分析原因:和上面几个案例的原因类似,不知不觉又搞了一个匿名内部类Runnable,对当前Activity都有一个隐式引用.如果Activity在销毁的时候,Runable内部的任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏.正确的做法还是使用静态内部类的方式,如下: 上面代码中,自定义了静态的内部类MyRunable,实现了Runable ,然后在使用的时候实例化它.

Facebook 的 iOS 内存泄漏监测自动化实践

内存是移动设备上的共享资源,如果一个 App 无法正确地进行内存管理的话,将会导致内存消耗殆尽,闪退以及性能的严重下降. Facebook 的 iOS 版本的许多功能模块共用了同一份内存空间,如果其中的某一个模块消耗了特别多的内存资源的话,将会对整个 App 造成严重影响.举个栗子,当某个功能模块不小心造成了内存泄漏的时候,这个情况就很有可能会发生. 在 Facebook,我们有非常多的工程师同时在一个代码仓库下进行并行开发.内存泄漏是在开发过程中难以避免会遇见的问题.当内存泄漏发生时,我们就需

BSTR使用误区以及隐藏的内存破坏和内存泄漏

作者:magictong 简介 BSTR的数据结构是什么样子并不是本文讨论的问题,但是却是本文的基础.在解决COM的跨平台编程的问题时,需要定义一种通用的字符串类型,它就这样被发明了,而且它的结构很容易匹配到不同的编程环境中,对于C++程序员来说,要记住的最基本的一点就是分配BSTR结构时,并不是简单的调用new.malloc就可以完成的,而且大部分的字符串相关的API和C库函数也是不能用于处理BSTR的,其实这也是使用BSTR的误区之一,在C++里面,BSTR被简单的define为wchar_