基于信号实现的Semaphore无法正常工作问题求助
信号实现Semaphore时线程阻塞无法唤醒的问题修复
问题描述
尝试用信号实现Semaphore验证临界区同步,预期输出如下:
Thread 2 trying to enter critical section... Thread 2 entered critical section. Thread 1 trying to enter critical section... Thread 2 leaving critical section. Thread 1 entered critical section. Thread 1 leaving critical section.
但实际运行后,线程1无法进入临界区,程序永久阻塞,输出停留在:
Thread 2 trying to enter critical section... Thread 2 entered critical section. Thread 1 trying to enter critical section... Thread 2 leaving critical section.
错误原因分析
核心问题是对sigsuspend()的工作机制理解偏差:
sigsuspend(&mask)的作用是临时将当前线程的信号掩码替换为mask,随后挂起等待信号;信号处理完成后,恢复原信号掩码并返回。- 你的代码中,
sem_down()创建的信号集包含SIGUSR1,调用sigsuspend(&set)时线程仍阻塞该信号,导致永远收不到唤醒信号,陷入死循环。 sem_init()通过pthread_sigmask全局阻塞了SIGUSR1,所有线程继承此掩码,必须通过sigsuspend临时解除阻塞才能接收信号。
修复后的代码
修正sigsuspend的信号集参数,并调整逻辑避免竞态:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <pthread.h> // 全局信号量变量,保证原子操作 volatile sig_atomic_t semaphore = 1; // 信号处理函数仅负责唤醒线程,不修改信号量 void signal_handler(int sig) {} // 初始化信号量与信号处理 void sem_init() { struct sigaction sa; sa.sa_handler = signal_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGUSR1, &sa, NULL); // 全局阻塞SIGUSR1,让线程通过sigsuspend临时解除阻塞 sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); pthread_sigmask(SIG_BLOCK, &set, NULL); } // 信号量P操作(等待) void sem_down() { sigset_t wait_mask; sigemptyset(&wait_mask); // 创建空信号集,临时解除SIGUSR1的阻塞 // 循环检查信号量,避免虚假唤醒 while (!semaphore) { sigsuspend(&wait_mask); } semaphore = 0; // 抢占信号量 } // 信号量V操作(释放) void sem_up() { semaphore = 1; // 释放信号量 kill(getpid(), SIGUSR1); // 向进程内所有线程发送唤醒信号 } // 线程函数:模拟临界区操作 void* thread_func(void* arg) { printf("Thread %ld trying to enter critical section...\n", (long)arg); sem_down(); printf("Thread %ld entered critical section.\n", (long)arg); sleep(2); // 模拟临界区工作 sem_up(); printf("Thread %ld leaving critical section.\n", (long)arg); return NULL; } int main() { sem_init(); pthread_t t1, t2; pthread_create(&t1, NULL, thread_func, (void*)1); pthread_create(&t2, NULL, thread_func, (void*)2); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; }
关键修正点
- sigsuspend信号集调整:使用空信号集,确保
sigsuspend临时解除对SIGUSR1的阻塞,线程能接收唤醒信号。 - 信号处理逻辑分离:将信号量的修改移到
sem_up中,避免信号处理函数与主逻辑的竞态问题。 - 保留循环检查:
while (!semaphore)避免虚假唤醒(如其他信号触发sigsuspend返回),确保只有信号量可用时才抢占。
验证结果
运行修复后的代码,输出符合预期,两个线程均能正常进入并退出临界区,程序可正常终止。
内容的提问来源于stack exchange,提问作者Itay Segev
相关产品推荐
相关产品推荐

