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

如何终止调用阻塞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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:08:13