我遇到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上的其他问题)明确地分离一个线程会阻止这个可能丢失的错误,但显然不是。有什么想法吗?
答案 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
中都有一个标志或类似的东西,告诉线程以受控方式关闭。