c - Unix pthreads 和信号 : per thread signal handlers

标签 c multithreading pthreads signals

我在让线程捕捉正确信号时遇到问题。

例如,

我首先启动一个主线程(tid 1)。

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

主线程创建一个新线程,tid 为 2。

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

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

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

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

此行为是有意为之,还是我需要更改某些内容才能使这些信号处理程序正常工作?

编辑:我做了一些调试,结果是信号被发送到线程 1,但是 function2() 出于某种原因从线程 1 被调用。有解决办法吗?

最佳答案

除了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;
}

我喜欢上面的,因为它不需要 pthreads,而且非常健壮和可靠。除了由于使用预处理器逻辑来选择使用哪种原子内置函数样式而造成的视觉困惑之外,如果您仔细观察,它也非常简单。

GCC 4.7 及更高版本提供类 C++11 __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。 (使用单个实时信号可以做到这一点,但双信号模型更易于实现和维护。)

这种信号反射或扇出有时很有用,它与这种方法没有什么不同。不过,如果有人感兴趣的话,差异足以保证自己的问题。

关于c - Unix pthreads 和信号 : per thread signal handlers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24643311/

相关文章:

用户级和内核级线程的并发性。为什么输出不同?

multithreading - 如何在经典asp中执行多线程/后台进程

c++ - sigqueue 可以与 pthreads 一起使用吗?

c - 如何继续循环程序直到输入 'y' 或 'n'

c - 数组的指针错误

C语言中的单词计数

c++ - 外部链接和 »extern "C"« block

java - 多线程应用程序中原子 boolean 值的使用

c++ - 这个简单的 pthread 代码的奇怪行为

c - 什么是线程池?