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
相关产品推荐
相关产品推荐

