如何终止调用阻塞std::getline()的C++线程?
解决阻塞getline线程的退出问题
核心问题是std::getline(std::cin, input)会一直阻塞等待输入,导致线程无法及时检查loop_status的变化。要解决这个问题,关键是打破getline的阻塞状态,让线程能回到循环条件判断。下面是几种可行的实现方案:
方案一:平台相关的非阻塞输入检查
在尝试读取输入前,先检查是否有可用的输入数据,同时穿插loop_status的判断。这种方法依赖平台提供的API:
Linux/macOS 版本
使用select()函数检查标准输入是否可读,设置超时时间,每次超时后都能检查loop_status:
#include <atomic> #include <thread> #include <iostream> #include <string> #include <sys/select.h> #include <unistd.h> std::atomic<bool> loop_status{false}; void getInput() { while (!loop_status) { fd_set readfds; FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); struct timeval timeout; timeout.tv_sec = 1; // 每秒检查一次loop_status timeout.tv_usec = 0; // 检查输入是否可读,超时返回0 int ret = select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, &timeout); if (ret == -1) { // 发生错误,退出循环 break; } else if (ret == 0) { // 超时,继续检查loop_status continue; } // 有输入可读,读取内容 std::string input; if (std::getline(std::cin, input)) { std::cout << input << std::endl; } } } void check() { while (!loop_status) { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 模拟触发退出条件 bool some_status = true; if (some_status) { loop_status = true; } } } int main() { std::thread t1(getInput); std::thread t2(check); t1.join(); t2.join(); return 0; }
Windows 版本
使用_kbhit()函数检查是否有按键输入:
#include <atomic> #include <thread> #include <iostream> #include <string> #include <conio.h> std::atomic<bool> loop_status{false}; void getInput() { while (!loop_status) { // 检查是否有输入 if (_kbhit()) { std::string input; if (std::getline(std::cin, input)) { std::cout << input << std::endl; } } // 短暂休眠,避免占用过多CPU std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void check() { while (!loop_status) { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); bool some_status = true; if (some_status) { loop_status = true; } } } int main() { std::thread t1(getInput); std::thread t2(check); t1.join(); t2.join(); return 0; }
方案二:发送信号中断阻塞的getline
当check线程触发退出条件时,向当前进程发送信号(比如SIGINT),让阻塞的getline被中断并返回错误,线程就能回到循环检查loop_status。
Linux/macOS 版本
#include <atomic> #include <thread> #include <iostream> #include <string> #include <signal.h> #include <unistd.h> std::atomic<bool> loop_status{false}; // 信号处理函数,仅用于中断getline void signalHandler(int signum) {} void getInput() { // 注册SIGINT信号处理函数 signal(SIGINT, signalHandler); while (!loop_status) { std::string input; // 被信号中断时,getline会返回false if (!std::getline(std::cin, input)) { // 清除错误状态,避免后续读取失败 std::cin.clear(); continue; } std::cout << input << std::endl; } } void check() { while (!loop_status) { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); bool some_status = true; if (some_status) { loop_status = true; // 向当前进程发送SIGINT信号,中断getline kill(getpid(), SIGINT); } } } int main() { std::thread t1(getInput); std::thread t2(check); t1.join(); t2.join(); return 0; }
方案三:用管道替换标准输入
创建一个管道,将标准输入重定向到管道的读端。当需要退出时,往管道的写端写入一个换行符,让getline读取到内容并返回,之后线程就能检查loop_status退出。
Linux/macOS 版本
#include <atomic> #include <thread> #include <iostream> #include <string> #include <unistd.h> #include <fcntl.h> std::atomic<bool> loop_status{false}; int pipe_fd[2]; // 管道文件描述符 void getInput() { // 保存原来的标准输入 int old_stdin = dup(STDIN_FILENO); // 将标准输入重定向到管道读端 dup2(pipe_fd[0], STDIN_FILENO); close(pipe_fd[0]); while (!loop_status) { std::string input; std::getline(std::cin, input); // 如果是触发退出的空行,跳过输出 if (loop_status) break; std::cout << input << std::endl; } // 恢复原来的标准输入 dup2(old_stdin, STDIN_FILENO); close(old_stdin); } void check() { close(pipe_fd[0]); // 关闭读端,只保留写端 while (!loop_status) { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); bool some_status = true; if (some_status) { loop_status = true; // 往管道写换行,唤醒getline write(pipe_fd[1], "\n", 1); close(pipe_fd[1]); } } } int main() { // 创建管道 pipe(pipe_fd); std::thread t1(getInput); std::thread t2(check); t1.join(); t2.join(); return 0; }
内容的提问来源于stack exchange,提问作者Julia.T
相关产品推荐
相关产品推荐

