多线程程序定时器异常:线程无限等待定时器信号排查请求
嘿,咱们来一步步拆解你遇到的多线程定时器卡壳问题——三个线程本该分别等待1、2、4秒,结果全无限挂起,大概率是信号掩码这块出了问题,尤其是你提到的sigemptyset和sigaddset的用法。
先明确核心逻辑:信号掩码是线程等待信号的关键
线程的信号掩码决定了哪些信号会被阻塞。如果你的线程是通过sigwait/sigwaitinfo等待定时器信号,那目标信号必须被添加到掩码中(也就是被阻塞)——否则信号会直接递达线程,要么触发默认处理(比如终止),要么被你注册的信号处理函数截走,sigwait永远收不到。
排查sigemptyset和sigaddset的常见错误
你提到的函数需要“创建与timer_signal中所选signal_number对应的信号掩码”,正确的步骤应该是这样的:
sigset_t timer_mask; // 1. 先初始化空信号集:这一步绝对不能省! // 未初始化的信号集内存是随机的,直接添加信号会导致掩码混乱 if (sigemptyset(&timer_mask) == -1) { perror("sigemptyset failed"); return -1; } // 2. 将目标信号添加到信号集 // 注意参数顺序:第一个是信号集,第二个是信号编号,别搞反! if (sigaddset(&timer_mask, signal_number) == -1) { perror("sigaddset failed"); return -1; } // 3. 把这个掩码设置到当前线程(阻塞目标信号,让sigwait能捕获) if (pthread_sigmask(SIG_BLOCK, &timer_mask, NULL) != 0) { perror("pthread_sigmask failed"); return -1; }
你可能踩的坑:
- 漏了
sigemptyset:直接调用sigaddset时,信号集里的初始值是随机的,可能包含无关信号,导致线程阻塞了不该阻塞的信号,或者没正确阻塞目标信号; sigaddset参数写反:把信号编号放在第一个参数、信号集放在第二个,这会直接调用失败;- 选了不能被阻塞的信号:比如
SIGKILL/SIGSTOP,这两个信号无法被阻塞、捕获或忽略,用它们做定时器信号肯定永远收不到。
额外排查点:定时器本身是否正确工作
除了信号掩码,还有两个关键环节会导致线程无限等待:
- 定时器的信号没发送到目标线程:
创建定时器时,要通过sigevent结构体指定信号发送给当前线程,比如Linux下要设置sev._sigev_un._tid = pthread_self();,否则信号可能被主线程或其他线程截走,当前线程永远等不到。 - 定时器没正确启动:
检查timer_settime的返回值是否为0,以及itimerspec的配置是否正确——比如it_value.tv_sec要设为你要等待的1/2/4秒,it_interval.tv_sec设为0(表示一次性定时器)。
给你一个可参考的正确片段
void* timer_thread(void* arg) { int wait_sec = *(int*)arg; timer_t timer; struct sigevent sev; sigset_t mask; struct itimerspec its; // 配置信号事件:让定时器信号发送给当前线程 sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGUSR1; // 用自定义信号,别用系统关键信号 sev._sigev_un._tid = pthread_self(); // Linux下指定接收线程 // 创建定时器 if (timer_create(CLOCK_REALTIME, &sev, &timer) == -1) { perror("timer_create failed"); pthread_exit(NULL); } // 设置信号掩码:阻塞SIGUSR1,确保sigwait能捕获 sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) { perror("pthread_sigmask failed"); timer_delete(timer); pthread_exit(NULL); } // 设置定时器触发时间 its.it_value.tv_sec = wait_sec; its.it_value.tv_nsec = 0; its.it_interval.tv_sec = 0; // 一次性触发 its.it_interval.tv_nsec = 0; if (timer_settime(timer, 0, &its, NULL) == -1) { perror("timer_settime failed"); timer_delete(timer); pthread_exit(NULL); } // 等待信号 int recv_sig; if (sigwait(&mask, &recv_sig) == 0) { printf("Thread %lu waited %d seconds, got signal %d\n", pthread_self(), wait_sec, recv_sig); } else { perror("sigwait failed"); } timer_delete(timer); pthread_exit(NULL); }
先按这个逻辑检查你的CreateAndArmTimer函数,重点确认sigemptyset和sigaddset的调用顺序、参数是否正确,再排查定时器的信号投递和启动配置,应该就能解决线程无限等待的问题了。
内容的提问来源于stack exchange,提问作者Ryan Wells
相关产品推荐
相关产品推荐

