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

C++多线程绘图卡顿且偶发崩溃问题技术求助

多线程游戏程序崩溃与显示不流畅问题

我设计了一个多线程游戏程序:

  • 1个线程负责车辆移动
  • 1个线程负责用户输入
  • 1个线程利用输入控制人物移动
    每完成一步后绘制车辆与人物。单线程模式下运行正常,但启用多线程后,程序有时会崩溃,且车辆与人物的显示不流畅。

绘图函数逻辑是擦除旧位置并在新位置重新绘制。

以下是我的代码:

void CGame::startGame()
{

    char user_input;
    bool input_ready = false;
    bool draw_finish = true;
    setGame();

    CPeople people;
    //g.runGame();


    char inp;
    condition_variable cv, cv2;
    mutex m;

    thread t1([&cv, &m, &user_input, &input_ready, &draw_finish, &cv2]() {

        while (true) {
            unique_lock<mutex> lock(m);
            cv2.wait(lock, [&draw_finish] {return draw_finish; });
            user_input = CConsole::getInput();
            input_ready = true;
            draw_finish = false;
            cv.notify_one();
        }
        });

    thread t3([&people, &cv, &m, &user_input, &input_ready, &draw_finish, &cv2]() {

        while (true) {
            unique_lock<mutex> lock(m);
            cv.wait(lock, [&input_ready] { return input_ready; });
            people.peopleMoving(user_input);
            //this_thread::sleep_for(chrono::milliseconds(10));
            draw_finish = true;
            input_ready = false;
            cv2.notify_one();
        }
        });
    //INPUT.get();
    mutex m2;
    thread t2([this, &m2, &cv, &draw_finish]() {
        //  unique_lock<mutex> lock(m);
        m2.lock();
    this->runObstacle();
    //this_thread::sleep_for(chrono::milliseconds(100));
    m2.unlock();


        });

    //while (true) {
    //  inp = CConsole::getInput();
    //  people.peopleMoving(inp);
    //  //std::this_thread::sleep_for(std::chrono::seconds());


    //}

    t1.join();
    t2.join();
    t3.join();
    //
}

问题分析与修复方案

1. 核心问题排查

  • 车辆线程逻辑失效:线程t2仅执行一次runObstacle()就终止,且直接调用m2.lock()而非RAII风格的unique_lock,一旦runObstacle()抛出异常,锁会永久无法释放,引发死锁风险。同时该线程与其他线程完全无同步,导致车辆移动和人物移动、绘图时序彻底混乱,这是显示卡顿的核心原因。
  • 控制台资源竞争:绘图操作(擦除/绘制)属于共享控制台资源,当前代码未对其加锁保护,多线程同时操作会导致输出错乱,甚至触发程序崩溃。
  • 输入线程锁阻塞:CConsole::getInput()在持有锁的状态下调用,若该函数是阻塞式等待用户输入,会导致其他线程完全无法获取锁,卡死整个同步流程。

2. 具体修复步骤

(1)重构车辆移动线程,加入同步逻辑

让车辆线程与其他线程保持时序同步,确保每一步都配合绘图流程:

// 替换原t2代码
thread t2([this, &m, &draw_finish, &cv2]() {
    while (true) {
        unique_lock<mutex> lock(m);
        cv2.wait(lock, [&draw_finish] { return draw_finish; });
        this->runObstacle(); // 执行车辆移动逻辑
        draw_finish = false;
        cv.notify_one(); // 通知人物处理线程
    }
});

(2)保护控制台绘图资源

在所有涉及控制台绘图的函数中,使用同一个互斥锁保护操作:

// 在CGame类中添加成员:mutex draw_mutex;
void CPeople::peopleMoving(char input) {
    unique_lock<mutex> lock(g_game->draw_mutex); // 假设g_game是CGame实例指针
    // 擦除旧位置代码
    // 计算新位置
    // 绘制新位置代码
}

void CGame::runObstacle() {
    unique_lock<mutex> lock(draw_mutex);
    // 擦除旧车辆位置代码
    // 移动车辆
    // 绘制新车辆位置代码
}

(3)调整输入线程的锁持有时机

将输入获取操作放到锁外,避免阻塞整个同步流程:

thread t1([&cv, &m, &user_input, &input_ready, &draw_finish, &cv2]() {
    while (true) {
        char input = CConsole::getInput(); // 先获取输入,不持有锁
        unique_lock<mutex> lock(m);
        cv2.wait(lock, [&draw_finish] {return draw_finish; });
        user_input = input;
        input_ready = true;
        draw_finish = false;
        cv.notify_one();
    }
});

(4)统一线程循环逻辑

确保三个线程持续循环,严格遵循「输入/车辆移动 → 人物移动 → 绘图完成 → 下一轮」的时序,避免混乱。

3. 额外优化建议

  • 抽离独立绘图线程:不要让业务线程各自处理绘图,统一由一个线程负责所有绘图操作,彻底消除控制台资源竞争。
  • 添加线程退出机制:设置bool running标志,游戏结束时所有线程检测到标志后退出循环,避免程序无法正常终止。

内容的提问来源于stack exchange,提问作者HCMUSer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:43:19