Linux下C++线程无限等待问题排查:乒乓式通信代码故障
排查乒乓线程通信的死锁问题
让我来帮你揪出这个死锁的根源,顺便把代码修复好——你的问题主要出在条件变量的使用规范上,还有初始同步的小问题:
核心问题分析
- 线程2违反条件变量的使用规则:
pthread_cond_wait要求调用者必须先持有对应的互斥锁,否则会触发未定义行为。你的线程2直接在未加锁的情况下调用pthread_cond_wait(&dudum, &mut),这会导致线程间的同步完全混乱,大概率触发死锁。 - 初始信号可能丢失:主线程启动两个线程后立刻发送
dadam信号,但此时线程1可能还没进入pthread_cond_wait的等待状态,这个信号会直接丢失,导致线程1一直阻塞等待,线程2也会因为错误的等待逻辑卡住。 - 缺少终止逻辑:原代码没有处理输入'e'或'E'的退出条件,会一直循环下去。
修复后的完整代码
#include <iostream> #include <string> #include <pthread.h> #include <cctype> // 包含toupper的头文件 using namespace std; pthread_mutex_t mut; pthread_cond_t dadam; // 通知线程1可以输入 pthread_cond_t dudum; // 通知线程2可以处理 char x; bool exit_flag = false; // 新增退出标记 void *first(void *arg) { while(1) { pthread_mutex_lock(&mut); // 等待线程2的通知(或者初始启动信号) pthread_cond_wait(&dadam, &mut); if (exit_flag) { // 检查是否需要退出 pthread_mutex_unlock(&mut); break; } cout << "Type a letter (enter 'e' to exit):\n"; cin >> x; // 检查是否触发退出条件 if (tolower(x) == 'e') { exit_flag = true; } pthread_mutex_unlock(&mut); pthread_cond_signal(&dudum); // 通知线程2处理字符 } return nullptr; } void *second(void *arg) { while(1) { pthread_mutex_lock(&mut); // 必须先加锁再调用wait pthread_cond_wait(&dudum, &mut); if (exit_flag) { // 检查退出标记 pthread_mutex_unlock(&mut); break; } char y = toupper(x); cout << "Converted to uppercase: " << y << endl; pthread_mutex_unlock(&mut); pthread_cond_signal(&dadam); // 通知线程1继续输入 } return nullptr; } int main() { // 初始化互斥锁和条件变量 pthread_mutex_init(&mut, nullptr); pthread_cond_init(&dadam, nullptr); pthread_cond_init(&dudum, nullptr); pthread_t nun, nuno; pthread_create(&nun, nullptr, &first, nullptr); pthread_create(&nuno, nullptr, &second, nullptr); // 给线程1发送初始信号,让它开始第一次输入 pthread_mutex_lock(&mut); // 加锁避免信号丢失 pthread_cond_signal(&dadam); pthread_mutex_unlock(&mut); // 等待线程结束 pthread_join(nun, nullptr); pthread_join(nuno, nullptr); // 清理资源 pthread_mutex_destroy(&mut); pthread_cond_destroy(&dadam); pthread_cond_destroy(&dudum); return 0; }
关键修改点说明
- 修正条件变量调用逻辑:线程2现在先调用
pthread_mutex_lock,再调用pthread_cond_wait,完全符合POSIX线程的规范。 - 避免初始信号丢失:主线程发送初始信号前先加锁,确保线程1已经进入等待状态后再发送信号(或者至少保证信号不会因为竞态条件丢失)。
- 添加退出逻辑:新增
exit_flag标记,当输入'e'或'E'时设置标记,让两个线程都能正常退出循环。 - 补充资源清理:原代码没有初始化和销毁互斥锁、条件变量,修复后补充了这些步骤,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Luke S
相关产品推荐
相关产品推荐

