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

循环中线程同步死锁求助:请求定位代码错误

线程同步屏障死锁问题定位

我希望每个线程在每次循环结束时完成同步,于是在循环末尾用条件变量实现了一个伪屏障,当其他线程未到达屏障时让当前线程睡眠,但程序始终死锁,求帮忙定位代码错误。

工作线程实现代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <pthread.h>
pthread_cond_t continue_cond;

pthread_mutex_t continue_mut;

pthread_mutex_t waiting_threads_mut;
int num_waiting_threads = 0;
pthread_mutex_t working_threads_mut;
int num_working_threads = 0;

int AllThreadsHere() {
    pthread_mutex_lock(&waiting_threads_mut);
    pthread_mutex_lock(&working_threads_mut);

    //printf("%d: %d\n", num_waiting_threads, num_working_threads);
    int res = (num_waiting_threads == num_working_threads) ? 1 : 0;
    pthread_mutex_unlock(&working_threads_mut);
    pthread_mutex_unlock(&waiting_threads_mut);
    return res;
}

void WorkerProcess(int* thread_id) {
    int to_process_indices = 1000;
    while (to_process_indices > 0) {
        // do computation here
        to_process_indices -= *(thread_id + 1);
        //
        pthread_cond_broadcast(&continue_cond);

        // increment number of waiting threads
        pthread_mutex_lock(&waiting_threads_mut);
        ++num_waiting_threads;
        pthread_mutex_unlock(&waiting_threads_mut);

        // this mutex is necessary so as to make the process of sleeping, and decrementing the number of waiting threads atomic.
            // note that if a thread wakes up from sleeping, then this mutex is locked again, meaning, the process of decrementing the number of working threads cannot occur without it unlocking.
                // this is very important, as an incoming thread may otherwise just finish its chunk
        pthread_mutex_lock(&continue_mut);

        while (AllThreadsHere() == 0) {
            //printf("Thread %d sleeping\n", args->thread_id);
            // waits for signal from incoming threads.
            pthread_cond_wait(&continue_cond, &continue_mut);
            //printf("Thread %d woken\n", args->thread_id);
        }

        pthread_mutex_unlock(&continue_mut);

        // need to decrease the number of waiting threads.
        pthread_mutex_lock(&waiting_threads_mut);
        --num_waiting_threads;
        pthread_mutex_unlock(&waiting_threads_mut);
    }

    pthread_mutex_lock(&continue_mut);
    pthread_mutex_lock(&working_threads_mut);
    --num_working_threads;
    pthread_cond_broadcast(&continue_cond);
    pthread_mutex_unlock(&working_threads_mut);
    pthread_mutex_unlock(&continue_mut);
}

主函数代码

int main() {
    const unsigned int NUM_THREADS = 3;
    const double PRECISION = 0.1;
    // make the space for worker threads.
    pthread_t* worker_threads = malloc(NUM_THREADS * sizeof(pthread_t));
    int* worker_ids = malloc(sizeof(int) * NUM_THREADS);

    pthread_cond_init(&continue_cond, NULL);
    pthread_mutex_init(&waiting_threads_mut, NULL);
    pthread_mutex_init(&working_threads_mut, NULL);
    pthread_mutex_init(&continue_mut, NULL);

    for (unsigned int k = 0; k < NUM_THREADS; ++k) {
        worker_ids[k] = k;
        pthread_create(worker_threads + k, NULL, WorkerProcess, (void*)(worker_ids + k));
    }

    for (unsigned int k = 0; k < NUM_THREADS; ++k) {
        pthread_join(worker_threads[k], NULL);
    }
}

核心错误点分析

1. num_working_threads 未初始化

主函数创建线程后,没有将num_working_threads设置为线程总数(3),导致AllThreadsHere()的判断条件num_waiting_threads == num_working_threads永远无法成立(初始num_working_threads为0),所有线程会一直卡在pthread_cond_wait中,直接引发死锁。

修复方式:在创建线程后添加初始化代码:

pthread_mutex_lock(&working_threads_mut);
num_working_threads = NUM_THREADS;
pthread_mutex_unlock(&working_threads_mut);

2. 数组越界访问触发未定义行为

WorkerProcess中to_process_indices -= *(thread_id + 1)存在严重问题:主函数中worker_ids是大小为NUM_THREADS的数组,每个线程传入的是worker_ids + k(指向第k个元素),当k为最后一个线程(索引2)时,thread_id +1会指向数组外的内存,触发未定义行为——可能破坏同步变量(如num_waiting_threads或num_working_threads的值),直接导致同步逻辑混乱。

修复方式:修改计算逻辑,比如用*thread_id代替*(thread_id +1),或确保数组访问不越界。

3. 条件变量广播时机错误

循环中刚完成计算就调用pthread_cond_broadcast(&continue_cond),此时还未更新num_waiting_threads,广播没有实际意义;正确的广播时机应该是最后一个线程到达屏障时,也就是更新num_waiting_threads后,判断如果所有线程都已到达,再广播唤醒其他线程。

修复方式:移除循环开头的广播,改为在更新num_waiting_threads后判断:

pthread_mutex_lock(&waiting_threads_mut);
++num_waiting_threads;
// 最后一个线程到达时广播
if (num_waiting_threads == num_working_threads) {
    pthread_cond_broadcast(&continue_cond);
}
pthread_mutex_unlock(&waiting_threads_mut);

4. 屏障判断逻辑的状态不一致问题

AllThreadsHere()中虽然加了锁,但解锁后返回结果的间隙,其他线程可能已经修改了num_waiting_threads或num_working_threads,导致判断结果失效。更合理的做法是在持有continue_mut锁的前提下,直接在循环内判断条件,避免函数调用带来的状态不一致。

修复方式:将判断逻辑移到WorkerProcess的循环中,避免额外函数调用:

while (1) {
    pthread_mutex_lock(&waiting_threads_mut);
    pthread_mutex_lock(&working_threads_mut);
    int all_here = (num_waiting_threads == num_working_threads);
    pthread_mutex_unlock(&working_threads_mut);
    pthread_mutex_unlock(&waiting_threads_mut);
    if (all_here) break;
    pthread_cond_wait(&continue_cond, &continue_mut);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:50:34