为什么pthread会导致内存泄漏

25

每当我创建一个pthread时,valgrind都会输出一个内存泄漏的警告信息。

例如下面这段代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h> 

void *timer1_function (void *eit){
  (void) eit;
    printf("hello world\n");
    pthread_exit(NULL);
}

int main(void){
   pthread_t timer1;
   pthread_create( &timer1, NULL, timer1_function,  NULL);  ///////line13
   int i=0;
   for(i=0;i<2;i++){usleep(1);}
   return 0;
}

Valgrind 输出

==1395== HEAP SUMMARY:
==1395==     in use at exit: 136 bytes in 1 blocks
==1395==   total heap usage: 6 allocs, 5 frees, 1,134 bytes allocated
==1395== 
==1395== 136 bytes in 1 blocks are possibly lost in loss record 1 of 1
==1395==    at 0x402A629: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1395==    by 0x4011304: allocate_dtv (dl-tls.c:297)
==1395==    by 0x4011AAB: _dl_allocate_tls (dl-tls.c:461)
==1395==    by 0x4052470: pthread_create@@GLIBC_2.1 (allocatestack.c:571)
==1395==    by 0x8048566: main (test.c:13)
==1395== 
==1395== LEAK SUMMARY:
==1395==    definitely lost: 0 bytes in 0 blocks
==1395==    indirectly lost: 0 bytes in 0 blocks
==1395==      possibly lost: 136 bytes in 1 blocks
==1395==    still reachable: 0 bytes in 0 blocks
==1395==         suppressed: 0 bytes in 0 blocks

尽管我参考了 man 手册,但为什么 pthread_create 函数会造成问题?我该如何解决?


5个回答

32

一个线程是一种已分配的资源,你在退出之前没有释放它。你应该调用 pthread_join;这也会消除你的hackish和不正确的睡眠循环的需要。

即使你解决了这个问题,valgrind仍然可能会看到"泄漏",因为一些POSIX线程的实现(我猜你正在使用glibc/NPTL)缓存并重复使用线程资源,而不是完全释放它们。我不确定valgrind是否会解决这个问题。


1
+1. 我认为你的第二段解释了为什么我在我的答案中经常看到抑制方法被使用。 - simonc
替代pthread_join的方法:使用pthread_detach,但内存泄漏仍然存在。 - Aubin

6
我认为valgrind在程序退出时分析您的程序状态,这可能是在线程完成执行之前:两微秒可能不足以向控制台输出"Hello, world!\n"。添加一个调用pthread_join应该可以解决这个泄漏问题。
pthread_join(timer1, NULL);

4
第二个参数可以直接传递一个空指针,而不是传递一个指向虚拟变量的指针。 - R.. GitHub STOP HELPING ICE
@R.. 是的,我想你是对的... OP正在将NULL传递给pthread_exit,所以pthread_join最好明确忽略结果。谢谢! - Sergey Kalinichenko

1

当我没有调用 pthread_join 时,我看到了类似的结果。

当我调用 pthread_join 时,Valgrind 将指示没有内存错误或泄漏。我使用 pthread_kill 来查看线程是否仍存在,然后调用 join 来清理和释放资源,这样可以得到干净的结果。

int
stop_worker(worker_t *self)
{
    if (self) {
        // signal the thread to quit
            // (here using a variable and semaphore)
        self->thread_quit=TRUE;
        sem_post(&self->sem);

        // wait for it to stop
        // (could use counter, etc. to limit wait)
        int test=0;
        while (pthread_kill(self->thread,0) == 0) {
            MDEBUG(MD_XF_LOGGER,"waiting for thread to exit...\n",test);
            delay_msec(50);
        }

        // even though thread is finished, need to call join
        // otherwise, it will not release its memory (and valgrind indicates a leak)
        test=pthread_join(self->thread,NULL);
        return 0;           
    }
    return -1;
}

1

泄漏问题与在子线程的本地存储(tls)中分配的DTV(动态线程向量)结构有关。

在主线程(即生成子线程的线程)中使用pthread_join()将确保修复泄漏。对于不需要pthread_join()调用的用例,使用子线程pthread_t调用pthread_detach可确保释放内存。

pthread_detach的手册中得知:

pthread_detach()函数标记由线程标识为已分离。当一个分离的线程终止时,它的资源将自动释放回系统,而无需另一个线程加入终止的线程。


0

内存泄漏是由于线程未被取消而持续运行,导致相应的动态分配内存未被释放。使用pthread_cancel()与pthread_cleanup_push(CleanupHandler, NULL)和pthread_cleanup_pop(0)一起进行线程清理以在取消后释放资源。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接