Unix pthreads和信号:每个线程信号处理程序

我无法让线程捕获正确的信号。

例如,

我首先开始一个主线程(tid 1)。

然后,它使用signal(2) SIGUSR1的信号处理程序设置为function1( signal(2)

主线程使用tid 2创建一个新线程。

在线程2中,我使用signal(2) SIGUSR1的信号处理程序注册到function2() signal(2)

线程1然后创建线程3(tid 3)。

从线程3,我使用pthread_kill(1, SIGUSR1)向线程1发送信号。

但是, function2()被调用,而不是function1()

这种行为是否有意,或者是否需要更改以使这些信号处理程序工作?

编辑:我已经完成了一些调试,结果是信号IS被发送到线程1,但是function2()由于某种原因从线程1调用。 这有解决方法吗?

除了alk的答案 :

您可以使用每线程函数指针以每线程方式选择在传递特定信号时执行的函数。

注意:信号将传递给未明确阻止其传递的任何线程。 这并没有改变这一点。 您仍然需要使用pthread_kill()或类似机制将信号定向到特定线程; 引发或发送到进程(而不是特定线程)的信号仍将由随机线程(在那些不阻塞它的线程中)处理。

我想不出任何个人喜欢这种方法的用例; 到目前为止,总有一些其他方式,其他更容易和更好的方式。 因此,如果您正在考虑为实际应用程序实现类似的function,请退后一步并重新考虑您的应用程序逻辑。

但是,既然这种技术是可行的 ,我可以通过以下方式实现它:

 #include  /* Per-thread signal handler function pointer. * Always use set_thread_SIG_handler() to change this. */ static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0; /* Process-wide signal handler. */ static void process_SIG_handler(int signum, siginfo_t *info, void *context) { void (*func)(int, siginfo_t *, void *); #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST); #else func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0); #endif if (func) func(signum, info, context); } /* Helper function to set new per-thread signal handler */ static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *)) { #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) __atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST); #else void (*oldfunc)(int, siginfo_t *, void *); do { oldfunc = thread_SIG_handler; } while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func)); #endif } /* Install the process-wide signal handler. */ int install_SIG_handlers(const int signum) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_sigaction = process_SIG_handler; act.sa_flags = SA_SIGACTION; if (sigaction(signum, &act, NULL)) return errno; return 0; } 

我喜欢上面因为它不需要pthreads,并且非常强大和可靠。 除了由于使用预处理器逻辑选择使用哪种样式的primefaces内置函数之外的视觉混乱,如果仔细观察它也很简单。

GCC 4.7及更高版本提供类似C ++ 11的__atomic内置函数 ,较旧的GCC版本和其他编译器(ICC,Pathscale,Portland Group)提供__sync遗留内置函数 。 线程本地存储的__thread关键字应该类似地在所有当前POSIX-y系统中可用。

如果您有一个过时的系统,或坚持遵守标准,则以下代码应具有大致相同的行为:

 #include  #include  #include  static pthread_key_t thread_SIG_handler_key; static void process_SIG_handler(int signum, siginfo_t *info, void *context) { void (*func)(int, siginfo_t *, void *); *((void **)&func) = pthread_getspecific(thread_SIG_handler_key); if (func) func(signum, info, context); } static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *)) { sigset_t block, old; int result; sigemptyset(&block); sigaddset(&block, SIG); /* Use signal number instead of SIG! */ result = pthread_sigmask(SIG_BLOCK, &block, &old); if (result) return errno = result; result = pthread_setspecific(thread_SIG_handler_key, (void *)func); if (result) { pthread_sigmask(SIG_SETMASK, &old, NULL); return errno = result; } result = pthread_sigmask(SIG_SETMASK, &old, NULL); if (result) return errno = result; return 0; } int install_SIG_handlers(const int signum) { struct sigaction act; int result; result = pthread_key_create(&thread_SIG_handler_key, NULL); if (result) return errno = result; sigemptyset(&act.sa_mask); act.sa_sigaction = process_SIG_handler; act.sa_flags = SA_SIGACTION; if (sigaction(signum, &act, NULL)) return errno; return 0; } 

我认为与我实际使用过的最接近真实生活的代码就是我使用一个实时信号( SIGRTMIN+0 )在除了一个线程之外的所有线程中被阻止的一个,作为reflection器:它发送了另一个实时信号( SIGRTMIN+1 )到多个工作线程,以便中断阻塞I / O. (可以使用单个实时信号执行此操作,但双信号模型实现起来更简单,更易于维护。)

这种信号reflection或扇出有时是有用的,并且与这种方法没有什么不同。 但是,如果有人感兴趣的话,不同的是保证自己的问题。

无法安装“每线程”信号处理程序。

man 7 signal (由我强调):

信号处理是一个每进程属性:在multithreading应用程序中,特定信号的配置对于所有线程都是相同的。

然而,通过在“每线程”基础上屏蔽任何数量的信号类型的接收,可以将每个信号类型引导到不同的线程。

关于如何将一组信号类型指向特定的线程,您可能希望看看这个答案: https : //stackoverflow.com/a/20728819/694576