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

基于信号实现的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;
}

关键修正点

  1. sigsuspend信号集调整:使用空信号集,确保sigsuspend临时解除对SIGUSR1的阻塞,线程能接收唤醒信号。
  2. 信号处理逻辑分离:将信号量的修改移到sem_up中,避免信号处理函数与主逻辑的竞态问题。
  3. 保留循环检查:while (!semaphore)避免虚假唤醒(如其他信号触发sigsuspend返回),确保只有信号量可用时才抢占。

验证结果

运行修复后的代码,输出符合预期,两个线程均能正常进入并退出临界区,程序可正常终止。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:58:21