Linux下如何用thrd_sleep实现主线程定时睡眠或被线程信号唤醒?
问题解答与实现示例
核心疑问解答
1. 是否需要配置信号处理程序?
必须配置。默认情况下,SIGUSR1/SIGUSR2这类用户信号的默认行为是终止进程,如果不修改信号处理逻辑,子线程发送信号会直接杀死整个程序。我们需要将目标信号的处理函数设为空函数(只捕获信号但不执行任何操作),这样thrd_sleep()收到信号时会提前返回,同时进程不会被终止。
2. 子线程如何向主线程发信号?选哪种信号?
- 信号选择:优先选
SIGUSR1或SIGUSR2,这两个是系统预留的用户自定义信号,不会被系统服务占用,适合用于线程间唤醒场景。 - 发送方式:主线程先通过
thrd_current()获取自己的线程ID(thrd_t类型)并传递给子线程,子线程调用pthread_kill()(C11的thrd库底层基于pthread实现,thrd_t等价于pthread_t)向主线程发送指定信号。
3. 主线程未睡眠时忽略信号?
通过动态切换信号处理逻辑实现:
- 主线程执行任务期间,将信号处理临时设为
SIG_IGN(忽略信号); - 进入睡眠前,恢复信号处理为之前的空函数,确保
thrd_sleep()能被信号中断; - 睡眠结束后,再次切换回忽略状态,继续执行任务。
完整实现示例
#include <stdio.h> #include <stdlib.h> #include <threads.h> #include <signal.h> #include <unistd.h> #include <time.h> // 存储主线程ID,供子线程调用 thrd_t main_thread_id; // 信号处理空函数:仅捕获信号,避免进程被终止 void signal_handler(int sig) { (void)sig; // 消除未使用变量警告 } // MQTT监听线程入口 int mqtt_listener(void *arg) { (void)arg; printf("MQTT listener thread started\n"); // 模拟MQTT消息监听逻辑,实际场景替换为MQTT客户端阻塞接收代码 while (1) { // 模拟每3分钟收到一条MQTT消息 sleep(180); printf("Received MQTT message, waking main thread...\n"); // 向主线程发送唤醒信号 pthread_kill((pthread_t)main_thread_id, SIGUSR1); } return 0; } int main() { struct sigaction sa_default, sa_ignore; thrd_t listener_thread; // 初始化信号处理:设置SIGUSR1的默认处理为空函数 sa_default.sa_handler = signal_handler; sigemptyset(&sa_default.sa_mask); sa_default.sa_flags = 0; sigaction(SIGUSR1, &sa_default, NULL); // 获取主线程ID main_thread_id = thrd_current(); // 创建MQTT监听线程 if (thrd_create(&listener_thread, mqtt_listener, NULL) != thrd_success) { perror("Failed to create listener thread"); exit(EXIT_FAILURE); } while (1) { // ========== 主线程执行业务任务阶段 ========== printf("Main thread executing tasks...\n"); // 临时忽略SIGUSR1,任务期间收到信号不处理 sa_ignore.sa_handler = SIG_IGN; sigemptyset(&sa_ignore.sa_mask); sa_ignore.sa_flags = 0; sigaction(SIGUSR1, &sa_ignore, NULL); // 模拟业务任务执行 sleep(10); printf("Main thread finished tasks, going to sleep for 5 minutes...\n"); // ========== 主线程进入睡眠阶段 ========== // 恢复信号处理,确保thrd_sleep可被中断 sigaction(SIGUSR1, &sa_default, NULL); struct timespec sleep_duration = {0}; sleep_duration.tv_sec = 5 * 60; // 5分钟睡眠时长 int sleep_result = thrd_sleep(&sleep_duration, NULL); if (sleep_result == -1) { printf("Main thread woken up by signal\n"); } else { printf("Main thread woke up after timeout\n"); } } // 实际场景需回收线程,此处省略 thrd_join(listener_thread, NULL); return 0; }
代码说明
- 信号逻辑切换:通过
sigaction动态调整SIGUSR1的处理策略,任务阶段忽略信号,睡眠阶段允许中断,兼顾业务执行的稳定性与唤醒需求。 - 线程通信:子线程通过主线程ID精准发送信号,避免干扰其他线程。
- MQTT模拟:示例中用
sleep(180)模拟消息接收,实际项目可替换为mosquitto_loop()等MQTT客户端阻塞API。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

