我的程序每次必须处理某些东西时都会分叉,并且在每个分叉的进程中我都会分离一个线程,以便从分叉进程中记录统计信息:这个线程循环来收集数据,但它没有实际条件来阻止这个循环。
我在"What happens to a detached thread when main() exits?"中读到:
如前所述,任何线程,无论是否分离,都将在大多数操作系统上以其进程而死亡。
在我的程序中,我没有为循环线程提供停止条件,因为当生成它的进程将死亡时,分离的线程将随之死亡。无论如何,我觉得我认为是理所当然的事情,所以我做了以下代码来简化我的疑问,并从我原来的程序中排除多余的。
在此代码中,每个分叉进程都会生成一个将打印一些数字的线程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <thread>
void threadFoo(int id) {
int i=0;
// this loop will simulate some stas collecting
while (i<1000000) {
printf("[%d]%d \t", id, i);
++i;
}
printf("\n\n\n");
return;
}
void forkFoo(int id) {
std::thread t(threadFoo, id);
t.detach();
printf("PID %d detached thread\n", getpid());
return;
}
int main(void) {
int i;
pid_t pid;
for (i=0; i<3; i++) {
pid = fork();
if (pid == 0) {
forkFoo(i);
// this sleep will simulate some work
sleep(1);
printf("Proc %d about to terminate...even its detached thread?\n");
_exit(EXIT_SUCCESS);
}
else if(pid > 0) {
// wait for all children to terminate
wait(NULL);
}
}
printf("main() about to terminate...\n");
}
程序的输出确认每个线程都以其进程终止
PID 13476 detached thread
[0]0 [0]1 [0]2 [0]3 ...
... [0]48940 [0]48941 Proc 13476 about to terminate...even its detached thread?
PID 13478 detached thread
[1]0 [1]1 [1]2 [1]3 ... [1]42395 [1]42396 Proc 13478 about to terminate...even its detached thread?
PID 13480 detached thread
[2]0 [2]1 [2]2 [2]3 ...
... [2]41664 [2]41665 Proc 13480 about to terminate...even its detached thread?
main() about to terminate...
当我使用valgrind --leak-check=full --show-leak-kinds=all
运行此程序时出现了一些疑问:当每个分叉进程终止时,valgrind
显示一些令人毛骨悚然的输出(13534是分叉进程PID):
==13534== HEAP SUMMARY:
==13534== in use at exit: 352 bytes in 2 blocks
==13534== total heap usage: 2 allocs, 0 frees, 352 bytes allocated
==13534==
==13534== 64 bytes in 1 blocks are still reachable in loss record 1 of 2
==13534== at 0x4C2B145: operator new(unsigned long) (vg_replace_malloc.c:333)
==13534== by 0x401DB5: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (new_allocator.h:104)
==13534== by 0x401CE1: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) (alloc_traits.h:351)
==13534== by 0x401B41: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >*, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr_base.h:499)
==13534== by 0x401A8B: std::__shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr_base.h:957)
==13534== by 0x401A35: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >::shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:316)
==13534== by 0x4019A9: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::allocate_shared<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:598)
==13534== by 0x401847: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::make_shared<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::_Bind_simple<void (*(int))(int)> >(std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:614)
==13534== by 0x401621: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::thread::_M_make_routine<std::_Bind_simple<void (*(int))(int)> >(std::_Bind_simple<void (*(int))(int)>&&) (thread:193)
==13534== by 0x4012AB: std::thread::thread<void (&)(int), int&>(void (&)(int), int&) (thread:135)
==13534== by 0x400F42: forkFoo(int) (funwiththreadinsidefork.cpp:21)
==13534== by 0x400FBD: main (funwiththreadinsidefork.cpp:36)
==13534==
==13534== 288 bytes in 1 blocks are possibly lost in loss record 2 of 2
==13534== at 0x4C2C9B4: calloc (vg_replace_malloc.c:711)
==13534== by 0x4012E14: allocate_dtv (dl-tls.c:296)
==13534== by 0x4012E14: _dl_allocate_tls (dl-tls.c:460)
==13534== by 0x5359D92: allocate_stack (allocatestack.c:589)
==13534== by 0x5359D92: pthread_create@@GLIBC_2.2.5 (pthread_create.c:500)
==13534== by 0x4EE8CAE: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==13534== by 0x4012D1: std::thread::thread<void (&)(int), int&>(void (&)(int), int&) (thread:135)
==13534== by 0x400F42: forkFoo(int) (funwiththreadinsidefork.cpp:21)
==13534== by 0x400FBD: main (funwiththreadinsidefork.cpp:36)
==13534==
==13534== LEAK SUMMARY:
==13534== definitely lost: 0 bytes in 0 blocks
==13534== indirectly lost: 0 bytes in 0 blocks
==13534== possibly lost: 288 bytes in 1 blocks
==13534== still reachable: 64 bytes in 1 blocks
==13534== suppressed: 0 bytes in 0 blocks
每个分叉进程死亡时的相同错误(警告?)消息。
最终输出是关于main()
进程,PID 13533:
==13533== HEAP SUMMARY:
==13533== in use at exit: 0 bytes in 0 blocks
==13533== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==13533==
==13533== All heap blocks were freed -- no leaks are possible
==13533==
==13533== For counts of detected and suppressed errors, rerun with: -v
==13533== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
我不知道如何读取所有这些valgrind输出,我不知道我的处理分离线程的方式是否正确:我正在使用C ++ 11,因为它没有提供垃圾收集器,我不知道那些possibly lost
和still reachable
字节是否会降低程序的性能;我经常fork()(即使每个分叉进程的生命周期都是几秒),每个分叉进程都会生成一个记录一些统计信息的分离线程。当forked进程终止时,线程就会死掉,但我不知道从长远来看,我的程序是否会因为valgrind向我显示的字节而减慢。
在您看来,我的担忧是否合理?我是否处理了分叉进程中分离线程的死亡?
答案 0 :(得分:2)
当您致电std::thread::detach
时,它不会将线程与进程分离,它只是将std::thread
实例与线程分离。它的堆栈是从进程中分配的。内存,它仍然与进程共享内存和资源:当进程停止时,它会将线程带出来。
它并没有优雅地完成,它没有被调用的任何析构函数,甚至它的堆栈被释放(这就是你看到泄漏的原因)。< / p>
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <atomic>
struct OnExit
{
const char* id = "none";
~OnExit()
{
std::cout << "Exiting " << id << std::endl;
}
};
thread_local OnExit onExit;
void threadFn1()
{
onExit.id = "threadFn1";
for (size_t i = 0; i < 100000; ++i) {
std::cout << onExit.id << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}
std::atomic<bool> g_running { true };
void threadFn2()
{
onExit.id = "threadFn2";
while (g_running) {
std::cout << onExit.id << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}
int main()
{
std::thread t1(threadFn1);
std::cout << "started t1\n";
t1.detach();
std::cout << "detached t1\n";
std::thread t2(threadFn2);
std::cout << "started t2\n";
std::this_thread::sleep_for(std::chrono::microseconds(500));
std::cout << "ending\n";
g_running = false;
t2.join();
}
现场演示:http://coliru.stacked-crooked.com/a/aa775a2960db09db
输出
started t1
detached t1
started t2
threadFn2
threadFn1
threadFn2
threadFn2
ending
Exiting threadFn2
因为我们自行终止threadFn2,所以它会调用OnExit
dtor,但是threadFn1会被粗暴地终止。
答案 1 :(得分:0)
“当进程死亡时,分叉进程内的分离线程会发生什么?” - 线程蒸发。线程存在于进程中。当进程终止时,其中的线程也会消失。
答案 2 :(得分:0)
线程死了。泄漏是由于线程没有完成运行并且没有释放自己的资源。如果你在主线程中的睡眠时间更长,那么就不会有任何泄漏。
阅读有关pcap的评论后编辑:
正确的方法:不要分开。给主线程访问所有pcap句柄。当主要想要停止它关闭所有pcaps并加入所有线程。当线程得到pcap错误时,它们退出。如果做得好,你就不会有泄漏和良好而干净的终止。
答案 3 :(得分:0)
该过程是保持您的线程的元素。当过程进行时,线程跟随。唯一的问题是你的线程是否获得了系统范围的资源,无论如何都需要释放。