pthreads:如何处理创建其他线程的主线程中的信号? (具体代码可见)
我有一个主线程,它保留在主函数中,即我没有像 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,它创建并加入所有其他线程,所以我不明白这段代码是否完全进入主函数,在主函数中完成/调用所有内容以启动程序。如果我将它放在一个线程上,并使用此代码和要清理的处理程序,这是否被视为忙等待?
I have a main thread, which stays in the main function, i.e. I do not create it specifically as in pthread_create, because it's not necessary. This thread opens a file, then creates other threads, waits for them to finish their work (i.e., does the join), cleans up everything (pointers, semaphores, conditional variables and so on...).
Now, I have to apply this code to block 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);
}
}
and it says You must use the main() function to launch the N+1 threads and wait for their completion. If a SIGINT signal arrives at the program it should be handled by the main thread in order to shutdown the program and its threads a clean way
My doubt is how should I put this code? Is it wrong to put it on a background thread created in main() ? Because I already have a cicle, with an exit flag, that creates and join all the other threads, so I don't understand if this code goes exactly to the main function where all is done/called to initiate the program. If I put it on a thread, with this code and the handler to clean, is this considerated as busy waiting?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
“它说”?说什么?家庭作业?
关于使用线程和信号进行编程,您应该了解的第一件事是,您几乎无法控制信号传递到哪个线程。如果您的主线程希望成为获取信号的线程,则它应该在创建任何新线程之前阻止该信号,并在完成创建它们后可能取消阻止它,以确保信号不会传递给它们。
但是,如果您遵循信号处理程序的最佳实践,那么哪个线程处理信号可能并不重要。信号处理程序应该做的就是设置一个全局标志或将一个字节写入管道(以最有效的方式让主线程注意到信号发生了。(请注意,您不能使用条件变量或信号处理程序中的任何锁定原语!)就像您问题中的代码片段一样,阻塞信号并使用 sigwait 也是可能的(再次注意,它需要在所有情况下被阻塞) 线程),但大多数程序不能仅仅停止并等待信号;它们还需要等待条件变量和/或来自文件的输入,解决此问题的一种方法是创建专用线程。调用
sigwait
,但这相当浪费。如果您已经在使用select
,更好的解决方案是切换到可以的pselect
。等待信号和文件描述符事件(同时),而不是向我们询问答案(在没有看到您尝试使用的完整程序的情况下很难给出答案)。尝试真正理解线程信号的复杂性会更好。
"It says"? What says? The homework assignment?
The first thing you should understand about programming with threads and signals is that you have very little control over which thread a signal is delivered to. If your main thread wants to be the one to get the signal, it should block the signal before creating any new threads and possible unblock it after it finishes creating them, to ensure that the signal is not delivered to them.
However, if you're following best practices for signal handlers, it probably doesn't matter which thread handles the signal. All the signal handler should do is set a global flag or write a byte to a pipe (whichever works best to get the main thread to notice that the signal happened. (Note that you cannot use condition variables or any locking primitives from signal handlers!) As in the code fragment in your question, blocking the signal and using
sigwait
is also possible (be aware, again, that it needs to be blocked in all threads), but most programs can't afford to stop and wait just for signals; they need to wait for condition variables and/or input from files as well. One way to solve this issue is to make a dedicated thread to callsigwait
, but that's rather wasteful. A better solution, if you're already usingselect
, would be to switch topselect
that can wait for signals as well as file descriptor events (at the same time).Rather than asking us for the answers (which would be hard to give anyway without seeing the full program you're trying to make this work with), you'd be much better off trying to really understand the intricacies of signals with threads.
关键步骤是调用
pthread_sigmask(SIG_BLOCK, &set, NULL)
,该调用必须在创建任何子线程之前发生。子线程将继承阻塞信号集,但只有在pthread_sigmask
调用之后创建它们时才会这样做。事实上,您可以将
sigwait
调用放入子线程中。那么问题来了,主线程如何知道子线程已经完成了呢?答:主线程必须加入子线程,并且一旦 sigwait 调用完成,子线程必须退出。所以整个练习就变得毫无意义。您也可以在主线程上调用 sigwait,因为在子线程上执行此操作不会带来任何好处。顺便说一句,除了阻塞和等待信号之外的任何信号仍然会传递到随机线程,因此默认处理程序实际上是不可靠的。例如,如果进程收到 SIGTRM 而不是 SIGINT,则行为是混乱的。 (systemd 使用 SIGTRM 来停止正在运行的服务)。
The key step is the call to
pthread_sigmask(SIG_BLOCK, &set, NULL)
, which has to occur before any child threads are created. Child threads will inherit the set of blocked signals, but will only do so if they are created after thepthread_sigmask
call.You can, in fact, put put the
sigwait
call in a child thread. But then then question becomes, how does the main thread know that the child thread has finished? Answer: the main thread has to join the child thread, and the child thread has to exit once the sigwait call completes. So the whole exercise becomes pointless. You may as well callsigwait
on the main thread, since you get no benefit from doing it on a child thread.In passing, any signal other than the blocked-and-waited-for signals still gets delivered to a random thread, so the default handler is actually unreliable. For example, if the process receives SIGTRM instead of SIGINT, the behaviour is chaotic. (SIGTRM is used by systemd to stop a running service).