除了alk's answer:
您可以使用每线程功能以某个线程的方式选择在某个信号传递时执行哪个函数的指针。
注意:信号传送到任何线程,不会明确阻止其传送。这不会改变这一点。您仍然需要使用pthread_kill()
或类似的机制将信号引导至特定的线程;发出或发送到进程(而不是特定线程)的信号仍将由随机线程处理(在不阻止它的那些线程中)。
我想不出任何用例,我个人更喜欢这种方法;到目前为止,一直有其他方式,其他方式更容易和更好。因此,如果您正在考虑为实际应用程序实施此类功能,请退后一步并重新考虑您的应用程序逻辑。
但是,因为该技术是可能,这里是如何我可能会实现它:
#include <signal.h>
/* 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;
}
我喜欢上面的,因为它不需要并行线程,并且非常稳定和可靠的。除了由于具有预处理器逻辑来选择使用哪种类型的原子内置插件而造成的视觉混乱之外,如果仔细观察,它也非常简单。
GCC 4.7及更高版本提供C++ 11-like __atomic built-ins,较老的GCC版本和其他编译器(ICC,Pathscale,Portland Group)提供__sync legacy built-ins。用于线程本地存储的__thread
keyword应该类似地在所有当前POSIX-y系统中可用。
如果你有一个陈旧的系统,还是坚持标准的符合性,下面的代码应该大致相当于行为:
#include <pthread.h>
#include <signal.h>
#include <errno.h>
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
)在一个线程中被阻塞,作为反射器:它发送另一个实时信号(SIGRTMIN+1
)给多个工作线程,以中断阻塞I/O。 (可以用一个实时信号做到这一点,但双信号模型实现起来更简单并且更容易维护。)
这样的信号反射或扇出有时是有用的,并且它没有什么不同这种方法。尽管如此,如果有人对此感兴趣,还是有足够的差别来保证自己的问题。
信号是一个进程调用,而不是每个线程一个,如果你调用它,它会设置进程中所有线程的处理程序。信号和线程是一个复杂的话题。你最好问自己该做什么。 –
在多线程程序中使用'signal'基本上是未定义的行为。它在手册中是这样说的。 –
源代码请。 –