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

如何在Rust中中断/取消io::stdin().read_line()的执行?

如何中断Rust中的io::stdin().read_line()读取

核心思路

Rust标准库的read_line()是阻塞式系统调用,本身不支持主动取消,但可以通过两种可靠方式实现中断:

  1. 利用信号中断系统调用:当进程收到SIGINT时,正在阻塞的read_line()会返回Interrupted错误,捕获该错误即可终止读取。
  2. 将标准输入设为非阻塞模式,结合轮询和终止标志,定期检查是否需要停止读取。

方案一:利用信号中断阻塞的read_line

通过signal_hook注册SIGINT信号,同时利用系统调用在信号到来时被中断的特性,终止读取循环。

修改后的代码:

use std::io::{self, Write};
use std::error::Error;
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use signal_hook::{consts::signal::SIGINT, flag};

fn main() -> Result<(), Box<dyn Error>> {
    let term = Arc::new(AtomicBool::new(false));
    // 注册SIGINT信号,触发时将终止标志设为true
    flag::register(SIGINT, Arc::clone(&term))?;

    loop {
        // 提前检查终止标志,避免在输出提示后阻塞
        if term.load(Ordering::Relaxed) {
            println!("\n收到SIGINT,终止程序");
            break;
        }

        print!("> ");
        io::stdout().flush()?;

        let mut line = String::new();
        match io::stdin().read_line(&mut line) {
            Ok(_) => {
                let command = line.trim().to_lowercase();
                if command == "exit" {
                    break;
                }
                println!("received: {}", line);
            }
            Err(e) if e.kind() == io::ErrorKind::Interrupted => {
                // 捕获SIGINT导致的中断错误,退出循环
                println!("\n读取被中断");
                break;
            }
            Err(e) => return Err(e.into()),
        }
    }

    Ok(())
}

说明

  • flag::register会在收到SIGINT时自动将原子布尔值设为true,无需额外轮询线程。
  • 阻塞的read_line()在收到信号时会直接返回Interrupted错误,无需等待输入完成。

方案二:非阻塞式读取结合轮询

通过nix crate将标准输入设为非阻塞模式,轮询读取输入内容,同时定期检查终止标志。

首先在Cargo.toml添加依赖:

[dependencies]
nix = "0.27"
signal-hook = "0.3"

实现代码:

use std::io::{self, Read, Write};
use std::error::Error;
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use std::thread;
use std::time::Duration;
use signal_hook::{consts::signal::SIGINT, flag};
use nix::fcntl::{fcntl, FcntlArg, O_NONBLOCK};

fn main() -> Result<(), Box<dyn Error>> {
    let term = Arc::new(AtomicBool::new(false));
    flag::register(SIGINT, Arc::clone(&term))?;

    // 将stdin设置为非阻塞模式
    let stdin_fd = io::stdin().as_raw_fd();
    fcntl(stdin_fd, FcntlArg::F_SETFL(O_NONBLOCK))?;

    print!("> ");
    io::stdout().flush()?;

    let mut buffer = String::new();
    let mut input_buffer = [0u8; 1024];

    loop {
        if term.load(Ordering::Relaxed) {
            println!("\n收到SIGINT,终止程序");
            break;
        }

        // 尝试读取输入
        match io::stdin().read(&mut input_buffer) {
            Ok(n) if n > 0 => {
                let s = String::from_utf8_lossy(&input_buffer[..n]);
                buffer.push_str(&s);
                // 检测到换行符时处理整行输入
                if buffer.contains('\n') {
                    let line = buffer.trim().to_string();
                    buffer.clear();
                    if line.to_lowercase() == "exit" {
                        break;
                    }
                    println!("received: {}", line);
                    print!("> ");
                    io::stdout().flush()?;
                }
            }
            Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
                // 无输入时短暂休眠,避免占用过多CPU
                thread::sleep(Duration::from_millis(100));
            }
            Err(e) => return Err(e.into()),
        }
    }

    Ok(())
}

说明

  • 非阻塞模式下,read不会一直等待输入,无内容时返回WouldBlock错误,此时可以检查终止标志。
  • 这种方式能随时响应SIGINT信号,无需等待输入操作完成。

注意事项

  • Rust不支持强制杀死线程,切勿尝试通过终止线程中断读取,会导致资源泄漏和未定义行为。
  • 方案一依赖Unix-like系统的信号机制,主流平台均支持;方案二需额外依赖nix,但控制更灵活。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:03:10