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

