如何在C++中实现符合条件的多线程临界区访问(火车轨道模拟)
解决火车轨道模拟中的并发同步问题

你正在开发的火车轨道模拟场景,核心并发约束很清晰:
- 每个轨道区段单向通行:同方向允许多辆火车,相反方向必须等当前方向所有火车离开后才能进入
- 火车进入区段后必须行驶至下一个枢纽,不能中途掉头
- 道岔/枢纽是无限制的等待区域,支持双向通行和避让
先聊聊你当前代码里的问题:
track_it->in_use == true;是比较操作而非赋值,根本没标记轨道为占用状态- 没有对轨道的状态变量做线程安全保护,多个线程同时操作会导致数据竞争
- 随机选择
target_id的逻辑不合理——火车只能从当前枢纽连接的轨道出发,不能随便选一个无关轨道 - 全局的
atomic_int lock完全没被使用,起不到同步作用
针对你的场景,mutex + 条件变量其实是最适合的方案(并非你担心的“不符合场景”)。我们可以给每个轨道区段增加同步和状态变量,来实现严格的单向通行控制。
具体实现方案
1. 扩展Track结构体,增加同步与状态字段
给每个轨道加上保护锁、条件变量,以及记录当前通行方向和火车数量的变量:
struct Track { int id; int point1; int point2; bool in_use; // 新增同步与状态变量 std::mutex track_mutex; std::condition_variable track_cv; int current_direction; // 0: 轨道为空, 1: point1→point2, -1: point2→point1 int train_count; // 构造函数初始化 Track(int id_, int p1, int p2, bool use = false) : id(id_), point1(p1), point2(p2), in_use(use), current_direction(0), train_count(0) {} };
2. 重写moveTrain函数,实现正确的并发逻辑
核心逻辑是:先确认火车的行驶方向,等待轨道满足进入条件(方向匹配或轨道为空),进入后更新状态,模拟行驶,离开时再更新状态并通知等待的火车。
bool Railroad::moveTrain(int id) { // 快速定位当前火车 auto train_it = std::find_if(trains.begin(), trains.end(), [id](const Train& t) { return t.getId() == id; }); if (train_it == trains.end()) return false; for(int i = 0; i < 10; i++){ std::this_thread::sleep_for(std::chrono::milliseconds(std::rand()%300)); // 从当前枢纽的连接轨道中随机选目标轨道(修正之前的随机逻辑) int current_junction = train_it->getLocation(); std::vector<Track*> available_tracks; for(auto& track : tracks){ if(track.point1 == current_junction || track.point2 == current_junction){ available_tracks.push_back(&track); } } if(available_tracks.empty()) continue; Track* target_track = available_tracks[std::rand() % available_tracks.size()]; // 确定行驶方向和目标枢纽 int direction = 0; int target_junction = 0; if(current_junction == target_track->point1){ direction = 1; target_junction = target_track->point2; } else { direction = -1; target_junction = target_track->point1; } // 等待并进入轨道 { std::unique_lock<std::mutex> lock(target_track->track_mutex); // 等待条件:轨道为空,或者方向与火车行驶方向一致 target_track->track_cv.wait(lock, [target_track, direction](){ return target_track->current_direction == 0 || target_track->current_direction == direction; }); // 更新轨道状态 if(target_track->current_direction == 0){ target_track->current_direction = direction; } target_track->train_count++; target_track->in_use = true; } // 自动释放锁 // 输出行驶日志(线程安全) { std::lock_guard<std::mutex> cout_lock(cout_mutex); std::cout << "Train " << id << " started moving on track " << target_track->id << " (direction: " << (direction == 1 ? "P1→P2" : "P2→P1") << ")" << std::endl; } // 模拟行驶时间 std::this_thread::sleep_for(std::chrono::milliseconds(std::rand()%1000)); // 离开轨道,更新状态 { std::unique_lock<std::mutex> lock(target_track->track_mutex); target_track->train_count--; // 如果是最后一辆火车离开,重置轨道方向并通知等待的线程 if(target_track->train_count == 0){ target_track->current_direction = 0; target_track->in_use = false; target_track->track_cv.notify_all(); } } // 更新火车位置 train_it->setLocation(target_junction); train_it->setMoving(false); // 输出到达日志(线程安全) { std::lock_guard<std::mutex> cout_lock(cout_mutex); std::cout<< "Train " << id << " has arrived at junction " << target_junction << " from track " << target_track->id << std::endl; } } return true; }
3. 调整main函数的Track初始化
使用新的构造函数创建轨道:
std::vector<Track> tracks; tracks.emplace_back(0, 0, 2); tracks.emplace_back(1, 2, 3); tracks.emplace_back(2, 3, 0); tracks.emplace_back(3, 0, 1); tracks.emplace_back(4, 1, 2); tracks.emplace_back(5, 1, 3);
为什么不用信号量?
信号量更适合控制资源的数量(比如限制同时进入的线程数),但你的场景需要基于方向的条件等待——不仅要知道轨道有没有空位,还要看方向是否匹配。条件变量可以更灵活地表达这种复杂的等待条件,配合mutex保护状态变量,完美解决你的问题。
额外优化建议
- 把全局的
cout_mutex放到Railroad类中作为成员变量,封装性更好 - 使用
std::random_device+std::mt19937替代std::rand(),生成更均匀的随机数 - 给Train和Track类增加更封装的接口,避免直接访问成员变量
内容的提问来源于stack exchange,提问作者codinglikejesus
相关产品推荐
相关产品推荐

