主线程/工作线程和信号处理

6
我正在编写一个程序,其中包含一个主线程和一些工作线程,并且我希望正确处理信号。我的问题如下:
- 主线程启动并进行所有分配。 - 主线程设置SIGINT信号处理程序。 - 主线程启动工作线程。工作线程不需要特殊的清理,但它们可以在系统调用或信号量上睡眠。
当接收到SIGINT时,我的理解是只有一个线程会接收到它。因此,如果线程正在系统调用或信号量上睡眠,则它们不会被唤醒,我将无法pthread_join我的工作线程并在主线程中执行所有必要的清理工作。
以下信号处理程序能否解决我的问题?
void term(int sig)
{
    g_do_cleanup = 1;
    pthread_kill(worker_1_id, some_other_signal);
    ...
    pthread_kill(worker_2_id, some_other_signal);
}

我希望的是,在收到SIGINT信号后,所有线程都会被另一个信号通知,退出它们的阻塞调用,查看标志并优雅地退出。欢迎提供任何有关如何正确实现此功能的评论或链接。
编辑: 我不是在寻找唤醒等待特定条件的多个线程的方法,因此我不认为pthread_cond_signal方法是我要寻找的。我想要的是:
- 找到一种方式,使所有被阻塞调用的线程从这些调用中返回。 - 或者杀死除主线程之外的所有线程。
3个回答

4

这是我通常在主应用程序线程中执行的操作:


/* before starting other threads */
sigset_t sigs;
sigemptyset( &sigs );
sigaddset( &sigs, SIGTERM );
sigaddset( &sigs, SIGINT );
/* add other signals to handle */
if ( pthread_sigmask( SIG_BLOCK, &sigs, 0 )) { /* handle error */ }
/* can start threads now */
...
/* in the main event loop */
siginfo_t info;
if ( sigwaitinfo( &sigs, &info ) == -1 ) { /* handle error */ }
switch( info.si_signo )
{
  case SIGTERM:
  case SIGINT: /* raise shutdown event */ break;
  default: /* other actions - log rotation, etc. */
}
...

这样,信号就变成了普通的应用程序事件 - 没有特殊的信号上下文限制等。通过sigtimedwait也可以使用超时进行等待,尽管BSD衍生版本不支持它。


2
对我来说,你想要的似乎是一个 pthread 条件变量,这样你就可以通过从主线程发出一个“事件”信号来唤醒多个线程中的任意数量。
请参阅PTHREAD_COND_DESTROYPTHREAD_COND_BROADCAST的 man 手册以获取更多信息。

是的,大多数人使用pthread或Win32/MFC实现来进行线程操作。 - Kieveli
不,我认为这不好,因为它不能唤醒我从阻塞的select调用或sem_wait调用中恢复。 也许我可以用pthread_condition替换我的信号量,但是信号量抽象确实符合我的同步模型。 - shodanex

1

你的计划看起来不错。你需要使用pthread-sigmask()在工作线程中屏蔽除了some-other-signal以外的系统信号,强制一个线程处理它们。

我认为我会选择一个单独的线程来处理进程信号而不是主线程。让它永远等待一个信号量或sigwait()或其他方式来等待运行信号处理程序。将pthread-kill代码移出信号处理程序。只需设置开关,让等待信号的线程向工作线程发送other_signal并退出即可。


使用信号在线程之间进行通信是错误的方法。许多线程库甚至不支持信号。最好使用线程库提供的线程同步机制,如条件变量。 - lothar
信号是原始的,远非理想,但他的库显然支持它们,否则他就不会提出这个问题。他所做的并没有什么是没有成功地完成过的。 - Duck
这不是关于线程通信的问题,而是关于线程终止的问题。pthread_cancel + 信号处理线程可能会起作用。 - shodanex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接