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

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

解决火车轨道模拟中的并发同步问题

轨道示意图

你正在开发的火车轨道模拟场景,核心并发约束很清晰:

  • 每个轨道区段单向通行:同方向允许多辆火车,相反方向必须等当前方向所有火车离开后才能进入
  • 火车进入区段后必须行驶至下一个枢纽,不能中途掉头
  • 道岔/枢纽是无限制的等待区域,支持双向通行和避让

先聊聊你当前代码里的问题:

  1. track_it->in_use == true; 是比较操作而非赋值,根本没标记轨道为占用状态
  2. 没有对轨道的状态变量做线程安全保护,多个线程同时操作会导致数据竞争
  3. 随机选择target_id的逻辑不合理——火车只能从当前枢纽连接的轨道出发,不能随便选一个无关轨道
  4. 全局的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:44:00