【问题标题】:pthread_create followed by pthread_detach still results in possibly lost error in Valgrindpthread_create 后跟 pthread_detach 仍然会导致 Valgrind 中可能丢失错误
【发布时间】:2010-12-31 02:51:51
【问题描述】:

我遇到了 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 的其他问题),明确分离线程可以防止这种可能丢失的错误,但显然不是。有什么想法吗?

【问题讨论】:

    标签: c multithreading memory memory-leaks pthreads


    【解决方案1】:

    glibc 的线程实现故意泄漏内存。它将分配给线程上下文的内存缓存起来,以便在下次创建线程时重用。我对没有缓存的实现进行了一些基准测试,似乎缓存将pthread_create 的最佳时间缩短了 50%,但大大减慢了pthread_join 的速度,导致净损失。当然,如果您关心的是线程创建延迟而不是吞吐量,这仍然是一个(小)收益。

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 这也是我要建议的,可能会测试pthread_cancel的返回值
      • 因此,如果任何线程(可能全部)在信号量上阻塞,取消将立即返回,但由于它们被阻塞,主服务器进程很可能在它们实际取消之前退出?
      • sem_wait 是一个取消点 (compute.cnr.berkeley.edu/cgi-bin/man-cgi?cancellation+5),所以如果线程是可取消的,等待应该立即取消。在无限循环中旋转的线程是更好的候选者。但是,您应该首先确定是否发生了这种情况,即线程是否被取消。
      • 我已经检查了 pthread_cancel() 的返回值(它是 0),并且应用程序在等待信号量的线程没有任何问题的情况下关闭。所以如果 sem_wait 是一个取消点,有没有办法确定线程实际上取消了,比如在线程函数的末尾放置一个 print 语句?或者取消会在调用 sem_wait 时停止执行?
      【解决方案3】:

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

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

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

      【讨论】:

      • 好点。我没有编写有问题的代码,我只是在寻找 Valgrind 中“可能丢失”消息的原因(和解决方法),但我也可能会在我处理它的时候修复它。
      猜你喜欢
      • 2019-06-23
      • 2019-01-24
      • 2011-04-05
      • 2016-08-28
      • 1970-01-01
      • 2017-09-14
      • 1970-01-01
      • 2015-05-31
      相关资源
      最近更新 更多