按ctrl+c
^Cctrl-c
terminate called without an active exception
Aborted (core dumped)
这是gdb stackstrace:
(gdb) bt
#0 0x0000003a47432625 in raise () from /lib64/libc.so.6
#1 0x0000003a47433e05 in abort () from /lib64/libc.so.6
#2 0x0000003a4a46007d in __gnu_cxx::__verbose_terminate_handler () at ../../.././libstdc++-v3/libsupc++/vterminate.cc:95
#3 0x0000003a4a45e0e6 in __cxxabiv1::__terminate (handler=<optimized out>) at ../../.././libstdc++-v3/libsupc++/eh_terminate.cc:47
#4 0x0000003a4a45e131 in std::terminate () at ../../.././libstdc++-v3/libsupc++/eh_terminate.cc:57
#5 0x000000000040172f in std::thread::~thread() ()
#6 0x00000000004036ad in void std::_Destroy<std::thread>(std::thread*) ()
#7 0x0000000000403396 in void std::_Destroy_aux<false>::__destroy<std::thread*>(std::thread*, std::thread*) ()
#8 0x000000000040311c in void std::_Destroy<std::thread*>(std::thread*, std::thread*) ()
#9 0x0000000000402dd6 in void std::_Destroy<std::thread*, std::thread>(std::thread*, std::thread*, std::allocator<std::thread>&) ()
#10 0x000000000040415b in std::vector<std::thread, std::allocator<std::thread> >::~vector() ()
#11 0x0000003a47435b22 in exit () from /lib64/libc.so.6
#12 0x000000000040142b in f(int) ()
#13 <signal handler called>
#14 0x0000003a478082fb in pthread_join () from /lib64/libpthread.so.0
#15 0x0000003a4a4bb627 in __gthread_join (__value_ptr=0x0, __threadid=<optimized out>)
at /root/tmp/gcc-4.9.3/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:668
#16 std::thread::join (this=<optimized out>) at ../../../.././libstdc++-v3/src/c++11/thread.cc:107
#17 0x0000000000401540 in t2() ()
#18 0x0000000000401585 in main ()
这是代码:
vector<thread*> v1;
vector<thread> v2;
void task1(std::string msg){
while (1) {
cout << "task1 says: " << msg << endl;
sleep(2);
}
}
void ctrl_c(int s)
{
cout << "ctrl-c\n";
exit(0);
}
void func1()
{
for (int i=0; i<3; i++) {
v1.push_back(new thread(task1, "v1"));
}
for (int i=0; i<3; i++) {
v1[i]->join();
}
}
void func2()
{
#ifndef GLOBAL
vector<thread> v2;
#endif
for (int i=0; i<3; i++) {
v2.push_back(thread(task1, "bad global v2"));
}
for (int i=0; i<3; i++) {
v2[i].join();
}
}
int main() {
signal(SIGINT,ctrl_c);
//func1();
//func2();
return 0;
}
请注意,v1是全局向量,包含线程指针; v2是全局向量,包含线程对象
当我只运行func1时,程序正常;
当我只运行func2时,根据是否在命令行上给出了GLOBAL选项,情况会有所不同。如果给出,编程正常,如果没有给出,将发生上述异常。此外,如果我注释掉signal(SIGINT,ctrl_c)
,ctrl + c将不会导致异常。(所以我猜exit
调用会导致全局矢量对象破坏,对吗?)
所以我的问题是:这些条件之间的差异是什么?在func2
条件下,如果我想捕获SIGINT并在其信号处理程序中调用exit
,同时我想使用全局vector<thread>
,那么在按下时我是如何避免异常的ctrl + c?
感谢
答案 0 :(得分:1)
查看回溯中的第12-14项。收到信号后,你在pthread_join()
(#12)内(#13)并且上下文切换到信号处理程序(#14)。然后从信号处理程序上下文中调用exit(0)
,但外部上下文仍在 pthread_join()
中。
例如,假设pthread_join()
对线程对象进行了锁定,对thread::~thread()
的调用尝试对同一线程对象进行锁定。 thread::~thread()
在信号处理程序上下文中等待获取在外部上下文中保存的锁...并且外部上下文不可能释放锁,直到执行从信号处理程序返回...并且你有资源僵局。
这不是在这里发生的事情(确切的问题是你的第一个评论者说的;在没有首先分离或加入的线程上调用terminate())。然而,这是一个非常常见的场景,并讲述了为什么在混合信号处理和线程时只需要小心的原因。
首先假设您不应 从信号处理程序中 在手册页中查找信号(man 7 signal
),您将找到可在信号处理程序中调用的安全函数列表。您会注意到_exit()
已列出但exit()
未列出。我将留给您阅读手册页,以确定_exit()
和exit()
之间的主要差异(这里有一个明显的区别)。
所以,不要再解释为什么你的错误代码行为不端的各种原因......我只是建议更合适地使用你的信号处理程序。
通常,我建议您只使用信号处理程序来设置全局标志变量的值。在信号处理程序上下文之外,您可以定期检查标志变量的值,以确定是否已收到信号。然后,您可以在外部上下文中处理信号,而不是在信号处理程序上下文中。
例如(只有一段代码片段,合理地插入代码中):
static volatile int sigterm_caught = 0;
void task1(std::string msg){
while (sigterm_caught == 0) {
cout << "task1 says: " << msg << endl;
sleep(2);
}
}
void ctrl_c(int s)
{
if(s == SIGTERM)
sigterm_caught = 1;
}
另请注意,sleep(2)
将在收到信号后提前返回。因此,一旦您的信号处理程序设置sigterm_caught = 1
并返回,while(!sigterm_caught)
条件将立即在所有线程中进行评估,您的代码很快就会在return 0
中由main()
正常退出。