我有一个主线程,它保留在主函数中,即我没有像 pthread_create 中那样专门创建它,因为它没有必要。该线程打开一个文件,然后创建其他线程,等待它们完成工作(即进行连接),清理所有内容(指针、信号量、条件变量等...)。
现在,我必须应用此代码来阻止 SIGINT:
sigset_t set;
int sig;
sigemptyset(&set);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, NULL);
while (1) {
sigwait(&set, &sig);
switch (sig) {
case SIGINT:
/* handle interrupts */
break;
default:
/* unexpected signal */
pthread_exit((void *)-1);
}
}
并且它说 您必须使用 main() 函数来启动 N+1 个线程并等待它们完成。如果 SIGINT 信号到达程序,则应由主线程处理它,以便以干净的方式关闭程序及其线程
我的疑问是我应该如何放置这段代码?将其放在 main() 中创建的后台线程上是否错误?因为我已经有一个带有退出标志的 cicle,它创建并加入所有其他线程,所以我不明白这段代码是否完全进入主函数,在主函数中完成/调用所有内容以启动程序。如果我将它放在一个线程上,并使用此代码和要清理的处理程序,这是否被视为忙等待?
“它说”?说什么?家庭作业?
关于使用线程和信号进行编程,您应该了解的第一件事是,您几乎无法控制信号传递到哪个线程。如果您的主线程希望成为获取信号的线程,则它应该在创建任何新线程之前阻止该信号,并在完成创建它们后可能取消阻止它,以确保信号不会传递给它们。
但是,如果您遵循信号处理程序的最佳实践,那么哪个线程处理信号可能并不重要。所有信号处理程序应该做的就是设置一个全局标志或将一个字节写入管道(以最有效的方式让主线程注意到信号发生了。(请注意,您不能使用信号中的条件变量或任何锁定原语)处理程序!)就像您问题中的代码片段一样,阻塞信号并使用
sigwait
也是可能的(再次注意,它需要在 all 线程中阻塞),但大多数程序无法承受停止并等待信号;他们还需要等待条件变量和/或来自文件的输入,解决此问题的一种方法是创建一个专用线程来调用 sigwait
,但这是一种更好的解决方案。如果您已经在使用 select
,则可以切换到 pselect
,它可以等待信号以及文件描述符事件(同时)。
与其向我们询问答案(在没有看到您尝试使用的完整程序的情况下很难给出答案),您最好尝试真正理解线程信号的复杂性。
关键步骤是对
pthread_sigmask(SIG_BLOCK, &set, NULL)
的调用,它必须在创建任何子线程之前发生。子线程将继承阻塞信号集,但只有在 pthread_sigmask
调用之后创建它们时才会这样做。
事实上,您可以将
sigwait
调用放入子线程中。那么问题来了,主线程如何知道子线程已经完成了呢?答:主线程必须加入子线程,并且一旦 sigwait 调用完成,子线程必须退出。所以整个练习就变得毫无意义。您也可以在主线程上调用 sigwait
,因为在子线程上执行此操作没有任何好处。
顺便说一句,除了阻塞和等待信号之外的任何信号仍然会被传递到随机线程,因此默认处理程序实际上是不可靠的。例如,如果进程收到 SIGTRM 而不是 SIGINT,则行为是混乱的。 (systemd 使用 SIGTRM 来停止正在运行的服务)。