在我的多线程GUI应用程序中,我有以下信号处理代码。我想改进这个代码,以便它是正确的和线程安全的,但有一些我在信号处理中不完全理解的东西:
void signal_handler(int sig)
{
switch (sig)
{
case SIGTERM:
::wxLogMessage(wxT("SIGTERM signal received ..."));
break;
case SIGINT:
::wxLogMessage(wxT("SIGINT signal received ..."));
break;
case SIGUSR1:
::wxLogMessage(wxT("SIGUSR1 signal received ..."));
break;
default:
::wxLogMessage(wxT("Unknown signal received ..."));
}
// send wxCloseEvent to main application window
::wxGetApp().GetTopWindow()->Close(true);
}
我在init函数中注册了信号处理程序:
// register signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
答案 0 :(得分:19)
在多线程应用程序中处理信号的一种简单方法是创建一个线程作为专用信号处理线程。所有感兴趣的信号都在每个线程中被阻止;没有建立信号处理程序;并且信号处理线程在循环中调用sigwaitinfo()
,在收到信号时对其进行处理。
这意味着您无需担心要调用的函数是否为 async-signal-safe ,因为信号处理程序不处理信号 - 它们是由您的专用信号处理线程同步处理,该线程可以调用它喜欢的任何函数(例如,它可以使用普通的pthreads同步函数来唤醒另一个线程)。
答案 1 :(得分:10)
要非常小心:正如signal(7)页面所说的那样,只有很少的函数(“async-signal-safe”)可以(直接或间接)在信号内部调用处理程序。可能不应在信号处理程序中调用互斥相关函数。另请参阅pthreads(7)
您可以考虑在信号处理程序中设置volatile sigatomic_t变量,并不时测试该标志的值。
如果你有C ++ 11(或C11)原子,例如C ++ 11 std::atomic或C11 <stdatomic.h>
,你可以使那个volatile
变量在这个意义上也是原子的。然后使用原子加载工具对其进行测试。
Qt文档建议following trick:在启动时为自己创建pipe(2),然后让您的信号处理程序write(2)(write
系统调用被指定为异步 - 信号安全)一个(或多个)字节[s]到同一进程的管道,并使你的GUI事件循环poll(2)成为该管道的读端。
使用Qt处理信号的特定于Linux的方式可能是signalfd(2)可能使用QSocketNotifier(尽管名称,它适用于可轮询文件描述符,而不是< em>仅套接字)。使用其他GUI工具包,您可能还可以添加要轮询的文件描述符(signalfd
或pipe
)。
答案 2 :(得分:4)
这个答案指的是POSIX线程(pthreads
)。
参考1:
可以在线程级别处理信号,是的。如果进程的多个线程处理信号并且信号被发送到进程,但是对于特定线程,则不确定哪个线程的处理程序将处理该信号。 (有关详细信息,请参阅man pthread_kill()
)
参考2:
信号处理程序将在设置它的线程的上下文中被执行。这包括主线程。
参考3:
如果将同一类型的多个信号发送到同一个进程,则在离开信号队列之前,它们可能只会压缩成一个信号。这是否可以区分我不确定的线程级别,我不得不承认。
参考4:
如果游戏中涉及共享资源:是的,至少对于同时访问这些资源的处理程序代码的部分。此外,这还取决于您尝试实施的逻辑。