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

C++ Hangman游戏多线程阻塞输入问题求助

解决C++多线程Hangman游戏输入线程阻塞问题

你的核心问题是输入线程卡在cin >> input的阻塞等待中,即使输出线程已经将gameEnded设为true,也必须等用户输入完成才能退出。以下是几种实用的解决思路:

1. 将标准输入设为非阻塞模式

通过修改终端属性,让输入不再一直阻塞,而是定期检查游戏状态,同时尝试读取输入。适合Unix/Linux和Windows平台(各自API不同)。

Unix/Linux示例代码:

#include <termios.h>
#include <unistd.h>
#include <chrono>
#include <thread>

// 设置终端为非阻塞输入模式
void setNonBlockingInput() {
    termios tty;
    tcgetattr(STDIN_FILENO, &tty);
    tty.c_lflag &= ~ICANON;  // 关闭规范模式,无需回车即可读取字符
    tty.c_cc[VMIN] = 0;      // 最小读取字符数设为0
    tty.c_cc[VTIME] = 1;     // 超时100毫秒
    tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}

// 恢复终端默认设置
void restoreTerminal() {
    termios tty;
    tcgetattr(STDIN_FILENO, &tty);
    tty.c_lflag |= ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}

void inputThread() {
    setNonBlockingInput();
    while (true) {
        if (gameEnded) {
            restoreTerminal();
            system("clear");
            break;
        }

        char c;
        // 尝试读取一个字符,非阻塞
        if (read(STDIN_FILENO, &c, 1) == 1) {
            input = c;
            if (input == '0') {
                restoreTerminal();
                system("clear");
                ended = true;
                break;
            }
            // 处理输入字符的逻辑...
            cv.notify_all();
        }
        // 短暂休眠,避免CPU空转
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

2. 用信号中断阻塞的输入线程

在Unix/Linux平台,可以给输入线程发送自定义信号,打断cin的阻塞状态,让线程有机会检查gameEnded标志。

示例代码:

#include <signal.h>
#include <pthread.h>
#include <limits>

std::atomic<bool> signalReceived(false);
pthread_t inputThreadId;

void signalHandler(int) {
    signalReceived = true;
}

void inputThread() {
    signal(SIGUSR1, signalHandler);
    inputThreadId = pthread_self();

    while (true) {
        if (gameEnded) {
            system("clear");
            break;
        }

        cin >> input;
        // 处理被信号中断的情况
        if (cin.fail() || signalReceived) {
            cin.clear(); // 清除输入流的失败标志
            // 忽略缓冲区残留的输入
            cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            signalReceived = false;
            continue;
        }

        if (input == '0') {
            system("clear");
            ended = true;
            break;
        }
        // 处理输入逻辑...
        cv.notify_all();
    }
}

// 输出线程中判定游戏结束时,发送信号唤醒输入线程
void outputThread() {
    while (true) {
        if (input == '0' || gameEnded) {
            system("clear");
            pthread_kill(inputThreadId, SIGUSR1);
            break;
        }
        // ...其他游戏逻辑
        update();
    }
}

3. 用select/定时等待实现非阻塞输入检查

利用系统调用select(Unix)或WaitForSingleObject(Windows),定期检查输入是否就绪,同时在每次检查间隙判断游戏是否结束。

Unix下select示例:

#include <sys/select.h>
#include <unistd.h>

void inputThread() {
    fd_set readfds;
    struct timeval tv;

    while (true) {
        if (gameEnded) {
            system("clear");
            break;
        }

        FD_ZERO(&readfds);
        FD_SET(STDIN_FILENO, &readfds);
        // 设置100毫秒超时
        tv.tv_sec = 0;
        tv.tv_usec = 100000;

        int ret = select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, &tv);
        if (ret == -1) {
            // 处理select错误,直接退出
            break;
        } else if (ret == 1) {
            // 有输入可读
            cin >> input;
            if (input == '0') {
                system("clear");
                ended = true;
                break;
            }
            // 处理输入逻辑...
            cv.notify_all();
        }
        // 超时则回到循环开头,检查gameEnded
    }
}

关键注意事项

  • 共享变量必须线程安全:gameEnded、input、ended这些被多线程读写的变量,一定要用std::atomic修饰,或者用互斥锁(std::mutex)保护,避免数据竞争导致线程看不到最新值。
  • 终端状态恢复:如果修改了终端属性,程序退出前一定要恢复默认设置,否则终端会出现输入异常(比如不需要回车就执行命令)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:15:18