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

C语言POSIX信号量多线程同步作业死锁问题求助

多线程同步死锁问题:线程10结束后剩余线程无法全部启动

作业要求

创建40个线程,并基于以下条件同步:

  • 主线程需等待所有其他线程结束后才能终止
  • 同一时刻最多6个线程处于运行状态
  • 线程10仅当包含自身在内的6个线程处于运行状态时才能终止
    补充定义:此处的“运行”指线程输出BEGIN日志后、END日志前的阶段,即线程10输出END时,必须恰好有另外5个线程已输出BEGIN并处于等待状态。

问题描述

使用POSIX信号量实现同步,已满足前两个条件,但线程10达成终止条件后,剩余线程无法全部启动,最终陷入死锁。

当前代码实现

全局变量与信号量初始化

#include <pthread.h>
#include <semaphore.h>

int p2_t_count = 0;
sem_t sem_t2;
sem_t sem_t210;
sem_t sem_kidnap;
// 注:缺少p2_t_count的同步锁

// 信号量初始化代码
if(sem_init(&sem_t2, 0, 5) < 0){
    perror("Error initializing the semaphore!");
    exit(2);
}

if(sem_init(&sem_t210, 0, 0) < 0){
    perror("Error initializing the semaphore!");
    exit(2);
}

if(sem_init(&sem_kidnap, 0, 0) < 0){
    perror("Error initializing the semaphore!");
    exit(2);
}

普通线程逻辑

void* thread_barrier(void* arg){
    struct thread_arg *thread_info = (struct thread_arg*)arg;

    // 劫持前5个线程,直到线程10结束才允许它们输出END
    if(p2_t_count < 5){
        sem_wait(&sem_t2);

        info(BEGIN, thread_info->process_n, thread_info->thread_n);

        p2_t_count++;
        if(p2_t_count == 5){
            // 当有5个线程进入运行状态时,解锁线程10
            sem_post(&sem_t210);
        }
        
        sem_wait(&sem_kidnap);
        info(END, thread_info->process_n, thread_info->thread_n);

        return NULL;
    }

    // 剩余34个线程的逻辑
    sem_wait(&sem_t2);

    info(BEGIN, thread_info->process_n, thread_info->thread_n);

    info(END, thread_info->process_n, thread_info->thread_n);
   
    sem_post(&sem_t2);

    return NULL;
}

线程10的逻辑

void* thread_t210(void* arg){
    sem_post(&sem_t210);
    info(BEGIN, 2, 10);
    sem_wait(&sem_t210);

    sem_wait(&sem_t210); // 此处多余的wait导致线程10阻塞
    info(END, 2, 10);

    // 释放被劫持的线程
    for(int i = 0; i < 5; i++){
        sem_post(&sem_kidnap);
    }

    // 尝试启动新线程
    for(int i = 0; i < 5; i++){
        sem_post(&sem_t2);
    }

    return NULL;
}

问题分析

  1. 线程10自身阻塞:线程10中存在多余的sem_wait(&sem_t210)调用。初始sem_t210值为0,线程10先执行sem_post使其变为1,随后第一次sem_wait将其重置为0,第二次sem_wait会导致线程10永久阻塞,无法执行到释放其他线程的逻辑,直接引发死锁。
  2. 全局变量竞争:p2_t_count的读写未加同步,多个线程可能同时判断p2_t_count <5并进入劫持分支,导致超过5个线程等待sem_kidnap,但后续仅post5次,剩余线程会永久等待。
  3. 信号量计数异常:由于前两个问题,线程10无法执行到sem_post(&sem_t2)的逻辑,剩余线程无法获取信号量启动。

解决方案

1. 修正线程10的逻辑

移除多余的sem_wait,让线程10等待前5个线程准备就绪后再启动:

void* thread_t210(void* arg){
    sem_wait(&sem_t210); // 等待前5个线程全部进入运行状态
    info(BEGIN, 2, 10);
    // 此时恰好有5个线程已输出BEGIN并等待sem_kidnap,加上线程10共6个运行线程
    info(END, 2, 10); // 满足终止条件

    // 释放被劫持的线程
    for(int i = 0; i < 5; i++){
        sem_post(&sem_kidnap);
    }

    // 补充sem_t2的名额(前5个线程未释放sem_t2)
    for(int i = 0; i < 5; i++){
        sem_post(&sem_t2);
    }

    return NULL;
}

2. 为p2_t_count添加同步锁

确保对p2_t_count的读写是原子操作,避免多个线程进入劫持分支:

// 添加全局互斥锁
pthread_mutex_t count_mutex;

// 初始化互斥锁(在信号量初始化后)
if(pthread_mutex_init(&count_mutex, NULL) != 0){
    perror("Error initializing mutex!");
    exit(2);
}

// 修改thread_barrier函数
void* thread_barrier(void* arg){
    struct thread_arg *thread_info = (struct thread_arg*)arg;
    int is_kidnap = 0;

    // 原子判断并更新计数
    pthread_mutex_lock(&count_mutex);
    if(p2_t_count < 5){
        is_kidnap = 1;
        p2_t_count++;
    }
    pthread_mutex_unlock(&count_mutex);

    if(is_kidnap){
        sem_wait(&sem_t2);

        info(BEGIN, thread_info->process_n, thread_info->thread_n);

        // 检查是否是第5个被劫持的线程
        pthread_mutex_lock(&count_mutex);
        int current_count = p2_t_count;
        pthread_mutex_unlock(&count_mutex);
        if(current_count == 5){
            sem_post(&sem_t210); // 解锁线程10
        }
        
        sem_wait(&sem_kidnap);
        info(END, thread_info->process_n, thread_info->thread_n);

        return NULL;
    }

    // 剩余线程逻辑不变
    sem_wait(&sem_t2);
    info(BEGIN, thread_info->process_n, thread_info->thread_n);
    info(END, thread_info->process_n, thread_info->thread_n);
    sem_post(&sem_t2);

    return NULL;
}

3. 确保主线程等待所有线程

主线程需要调用pthread_join等待所有40个线程终止,例如:

pthread_t tids[40];
// 创建所有线程(包括线程10)的代码...

// 等待每个线程结束
for(int i = 0; i < 40; i++){
    pthread_join(tids[i], NULL);
}

// 销毁信号量和互斥锁
sem_destroy(&sem_t2);
sem_destroy(&sem_t210);
sem_destroy(&sem_kidnap);
pthread_mutex_destroy(&count_mutex);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 22:17:27