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

ale*_*lin 6 c memory multithreading 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)
Run Code Online (Sandbox Code Playgroud)

创建这些线程的代码(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);
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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

R..*_*R.. 8

glibc的线程实现故意泄漏内存.它保持分配给线程上下文的内存,以便在下次创建线程时重用.我做了一些基准测试而不是没有缓存的实现,看起来缓存可以减少50%的最佳时间pthread_create,但是会大幅减慢pthread_join,因为净损失.当然,如果你关心的是线程创建延迟而不是吞吐量,它仍然是(小)增益.

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

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


Mar*_*wis 4

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