如何修复多线程抢椅子游戏中的"terminate called without an active exception"错误
多线程抢椅子游戏"terminate called without an active exception"错误分析与修复
错误原因
- 代码缩进逻辑错误,导致互斥锁未正确释放:
musical_chairs函数中else分支仅包裹了not_capture(idx);,后续的玩家移除循环和闭合大括号属于游离代码,这会导致部分线程执行路径中mtx.unlock();无法被调用,互斥锁长期被持有,同时线程退出时未完成解锁操作,触发终止异常。 - 线程对象未正确管理:当线程执行到错误分支提前退出时,线程对象在销毁前未被
join,C++标准规定可join状态的线程对象被销毁时会调用std::terminate,这就是错误的直接根源。 - 玩家列表遍历的越界隐患:原来的循环用
remaining_player_num作为终止条件,但players在erase后大小会减小,容易导致越界访问。
修复方案
1. 修正代码块结构,保证锁的正确释放
把else分支的所有逻辑用大括号包裹,确保mtx.unlock();在所有执行路径下都能触发:
void musical_chairs(IntQueueHW6 &my_queue, unsigned int idx,unsigned int remaining_player_num, vector<int> &players) { this_thread::sleep_until(chrono::system_clock::now() + chrono::seconds(2)); mtx.lock(); if(!(my_queue.isFull())) { my_queue.enqueue(idx); cout << "Player " << idx << " captured a chair at "; current_time(); cout << "." << endl; } else { not_capture(idx); for(auto it = players.begin(); it != players.end(); ++it) { if(*it == idx) { players.erase(it); break; } } } mtx.unlock(); }
2. 使用RAII锁替代手动锁操作(更安全)
用std::lock_guard自动管理锁的生命周期,彻底避免手动解锁遗漏的问题:
#include <mutex> std::mutex mtx; // 确保全局互斥锁正确声明 void musical_chairs(IntQueueHW6 &my_queue, unsigned int idx,unsigned int remaining_player_num, vector<int> &players) { this_thread::sleep_until(chrono::system_clock::now() + chrono::seconds(2)); std::lock_guard<std::mutex> lock(mtx); // 构造时自动加锁,销毁时自动解锁 if(!(my_queue.isFull())) { my_queue.enqueue(idx); cout << "Player " << idx << " captured a chair at "; current_time(); cout << "." << endl; } else { not_capture(idx); for(auto it = players.begin(); it != players.end(); ++it) { if(*it == idx) { players.erase(it); break; } } } }
3. 修正线程容器的创建与join逻辑
main函数中,线程容器不需要按初始玩家数创建,按当前剩余玩家数创建即可,避免无效索引,同时简化join逻辑:
while(remaining_player_num != 1) { IntQueueHW6 my_queue(total_round_num); // 按当前剩余玩家数创建线程容器 vector<thread> threads; threads.reserve(players.size()); cout << "Time is now "; current_time(); cout << endl; for(int idx : players) { threads.emplace_back(&musical_chairs, ref(my_queue), idx, remaining_player_num, ref(players)); } // 遍历所有线程完成join,确保没有线程处于可join状态被销毁 for(auto& t : threads) { if(t.joinable()) { t.join(); } } display_remaining_players(players); remaining_player_num --; total_round_num -- ; my_queue.clear(); }
核心问题总结
你遇到的terminate called without an active exception主要是因为线程对象在可join状态下被销毁,而根源是代码缩进错误导致锁未正确释放、线程执行路径异常。通过修正代码块结构、使用RAII锁、正确管理线程生命周期,就能彻底解决这个问题。
内容的提问来源于stack exchange,提问作者Atacan
相关产品推荐
相关产品推荐

