You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保子线程触发的Signal由主线程处理?

让信号处理始终在主线程执行的解决方案

要实现信号无论由外部触发还是子线程触发,都由主线程处理,核心思路是让所有子线程屏蔽目标信号,仅主线程保留接收权限,这样内核只会将信号递送到主线程,同时不需要额外创建线程。以下是两种可行方案:

方案一:基于信号掩码+信号处理函数

通过设置信号掩码,让子线程继承屏蔽SIGINT的配置,仅主线程解除屏蔽,确保信号处理函数在主线程执行:

#include <csignal>
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <pthread.h>
#include <unistd.h>

std::thread::id main_id;
std::mutex cout_mutex; // 避免多线程输出混乱

void signal_handler(int sig) {
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << std::this_thread::get_id() << " got signal\n";
    // 恢复默认信号处理,避免重复触发
    signal(sig, SIG_DFL);
    exit(sig);
}

void thread_fn(pthread_t main_thread) { 
    while (true) { 
        {
            std::lock_guard<std::mutex> lock(cout_mutex);
            std::cout << "subthread " << std::this_thread::get_id() << " printing\n";
        }
        std::this_thread::sleep_for(std::chrono::seconds(5));
        // 向主线程发送SIGINT,而非给自己发送
        pthread_kill(main_thread, SIGINT);
    } 
}

int main() { 
    // 1. 主线程先屏蔽SIGINT
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    pthread_sigmask(SIG_BLOCK, &mask, nullptr);

    // 2. 注册信号处理函数
    signal(SIGINT, signal_handler);

    // 3. 获取主线程的pthread句柄,传递给子线程
    pthread_t main_thread = pthread_self();
    main_id = std::this_thread::get_id();

    // 4. 创建子线程,子线程自动继承信号掩码(屏蔽SIGINT)
    std::thread t(&thread_fn, main_thread);

    // 5. 主线程解除SIGINT屏蔽,成为唯一能接收该信号的线程
    pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);

    while (true) { 
        {
            std::lock_guard<std::mutex> lock(cout_mutex);
            std::cout << "main " << main_id << " printing\n";
        }
        std::this_thread::sleep_for(std::chrono::seconds(5));
    } 

    t.join();
    return 0;
}

关键说明:

  • 子线程继承主线程的信号掩码,因此无法接收SIGINT,避免信号处理函数在子线程执行
  • 外部Ctrl+C触发的SIGINT会被内核递送到唯一未屏蔽该信号的主线程
  • 子线程需要触发信号时,通过pthread_kill直接向主线程发送,而非使用raise给自己发送

方案二:基于sigwait(更安全)

sigwait可以让主线程在循环中主动等待信号,避免在信号处理函数中调用非异步安全的函数(如cout),更符合POSIX安全规范:

#include <csignal>
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <pthread.h>
#include <errno.h>

std::thread::id main_id;
std::mutex cout_mutex;

void thread_fn(pthread_t main_thread) { 
    while (true) { 
        {
            std::lock_guard<std::mutex> lock(cout_mutex);
            std::cout << "subthread " << std::this_thread::get_id() << " printing\n";
        }
        std::this_thread::sleep_for(std::chrono::seconds(5));
        pthread_kill(main_thread, SIGINT);
    } 
}

int main() { 
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    // 主线程屏蔽SIGINT,子线程继承该配置
    pthread_sigmask(SIG_BLOCK, &mask, nullptr);

    pthread_t main_thread = pthread_self();
    main_id = std::this_thread::get_id();
    std::thread t(&thread_fn, main_thread);

    int sig;
    struct timespec timeout = {5, 0}; // 5秒超时,匹配原sleep逻辑
    while (true) { 
        {
            std::lock_guard<std::mutex> lock(cout_mutex);
            std::cout << "main " << main_id << " printing\n";
        }
        // 等待SIGINT,超时则继续循环
        int ret = sigtimedwait(&mask, nullptr, &timeout);
        if (ret == SIGINT) {
            std::lock_guard<std::mutex> lock(cout_mutex);
            std::cout << main_id << " got signal\n";
            exit(SIGINT);
        } else if (ret == -1 && errno == EAGAIN) {
            continue;
        }
    } 

    t.join();
    return 0;
}

关键说明:

  • 主线程通过sigtimedwait等待信号,超时时间与原逻辑的sleep一致,不阻塞程序正常执行
  • 无需注册信号处理函数,所有信号处理逻辑都在主线程的循环中完成,避免异步安全问题

内容的提问来源于stack exchange,提问作者nothingisme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:35:58