如何在Rust中中断/取消io::stdin().read_line()的执行?
如何中断Rust中的
io::stdin().read_line()读取 核心思路
Rust标准库的read_line()是阻塞式系统调用,本身不支持主动取消,但可以通过两种可靠方式实现中断:
- 利用信号中断系统调用:当进程收到SIGINT时,正在阻塞的
read_line()会返回Interrupted错误,捕获该错误即可终止读取。 - 将标准输入设为非阻塞模式,结合轮询和终止标志,定期检查是否需要停止读取。
方案一:利用信号中断阻塞的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
相关产品推荐
相关产品推荐

