pthread_create后跟pthread_detach仍会导致Valgrind中可能丢失的错误

时间:2010-12-31 02:51:51

标签: c multithreading memory memory-leaks pthreads

我遇到Valgrind的问题,告诉我可能会丢失一些内存:

==23205== 544 bytes in 2 blocks are possibly lost in loss record 156 of 265
==23205==    at 0x6022879: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23205==    by 0x540E209: allocate_dtv (in /lib/ld-2.12.1.so)
==23205==    by 0x540E91D: _dl_allocate_tls (in /lib/ld-2.12.1.so)
==23205==    by 0x623068D: pthread_create@@GLIBC_2.2.5 (in /lib/libpthread-2.12.1.so)
==23205==    by 0x758D66: MTPCreateThreadPool (MTP.c:290)
==23205==    by 0x405787: main (MServer.c:317)

创建这些线程的代码(MTPCreateThreadPool)基本上会获取一个等待pthread_t槽的块的索引,并创建一个带有该线程的线程。 TI成为指向具有线程索引和pthread_t的结构的指针。 (简化的/消毒):

for (tindex = 0; tindex < NumThreads; tindex++)
  {
  int rc;
  TI = &TP->ThreadInfo[tindex];
  TI->ThreadID = tindex;

  rc = pthread_create(&TI->ThreadHandle,NULL,MTPHandleRequestsLoop,TI);
  /* check for non-success that I've omitted */
  pthread_detach(&TI->ThreadHandle);
  }

然后我们有一个函数MTPDestroyThreadPool,它遍历我们创建的所有线程并取消它们(因为MTPHandleRequestsLoop不会退出)。

for (tindex = 0; tindex < NumThreads; tindex++)
  {
  pthread_cancel(TP->ThreadInfo[tindex].ThreadHandle);
  }

我在其他地方读过(包括SO上的其他问题)明确地分离一个线程会阻止这个可能丢失的错误,但显然不是。有什么想法吗?

3 个答案:

答案 0 :(得分:8)

glibc的线程实现故意泄漏内存。它保留分配给线程上下文的内存,以便在下次创建线程时重用。我做了一些基准测试而不是没有缓存的实现,似乎缓存削减了pthread_create的最佳时间50%的折扣,但是显着减慢了pthread_join的净损失。当然,如果你关心的是线程创建延迟而不是吞吐量,它仍然是一个(小的)收益。

另请注意,分离的线程很难释放其上下文,即使它想要。使用可连接线程,调用pthread_join的线程可以解除分配上下文,但是在解除分配其上下文和终止自身之间的间隔期间,分离的线程必须能够在没有堆栈的情况下操作。这只能通过在纯asm中编写一小段代码来实现。

想知道在没有类似竞争条件的情况下,分离线程的上下文如何返回到缓存? Linux具有在线程终止时将特定地址(由用户空间线程库注册)清零int的功能。因此,线程可以安全地将自己的上下文添加到缓存中,因为在它终止之前,其他线程仍会在此地址处看到非零值(通常是其thread-id)并将其解释为表示上下文仍在使用中。 / p>

答案 1 :(得分:4)

一个原因可能是pthread_cancel实际上没有取消线程 - 它不能保证。线程取消是异步的; pthread_cancel立即返回,但取消可能会推迟到下一个取消点。在这种情况下,当Valgrind收集统计信息时,线程可能仍然存在。

答案 2 :(得分:0)

创建一个可连接的线程以立即分离它对我来说没什么意义。这只会给系统带来开销。

我从一开始就启动了分离的线程,无论如何都有共享的ThreadInfo数据结构来控制你的线程。

此外,我在每个线程参数ThreadInfo中都有一个标志或类似的东西,告诉线程以受控方式关闭。