Rust tokio_serial中readable()不阻塞致CPU占用100%问题排查
tokio_serial接收数据后CPU占用100%的问题分析与解决
你的代码核心问题在于:当port.try_read()返回WouldBlock错误(即当前无有效可读数据,但之前的可读事件被触发)时,你直接忽略错误进入下一次循环,导致port.readable().await反复立即返回,形成无限空循环,CPU被拉满。
问题根源
串口的就绪通知(readable)在某些场景下会持续触发,比如第一次读取后驱动未正确清除就绪状态,或者try_read未完全消费就绪事件。此时如果不处理WouldBlock错误,循环会无休无止地执行readable().await和try_read(),完全占用CPU资源。
解决方案
最简洁可靠的方式是使用tokio::io提供的异步读写扩展方法,它们会自动处理等待就绪、重试等逻辑,无需手动管理readable()状态。
修正后的代码
use std::{env, process, str}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio_serial::{self, SerialPortBuilderExt}; #[tokio::main(flavor = "current_thread")] async fn main() { let baud_rate: u32 = 115200; let serial_port = env::args().nth(1).unwrap_or_else(|| String::from("/dev/ttyUSB0")); println!("Open port {serial_port}"); let mut port = tokio_serial::new(serial_port, baud_rate) .open_native_async() .unwrap_or_else(|e| { println!("Error: {:?}", e); process::exit(-1); }); let mut buf: [u8; 255] = [0; 255]; loop { match port.read(&mut buf).await { Ok(size) if size > 0 => { println!("Got {} bytes: {}", size, str::from_utf8(&buf[0..size]).unwrap()); // 使用write_all确保所有数据写入,避免部分写入 if let Err(e) = port.write_all(&buf[0..size]).await { println!("Write error: {:?}", e); } } Ok(0) => { // 串口连接关闭 println!("Serial port closed"); break; } Err(e) => { println!("Read error: {:?}", e); // 根据需求选择重试或退出 // break; } } } }
关键改进点
- 使用
AsyncReadExt::read替代手动管理就绪状态:read().await会自动等待串口可读,直到有数据或发生错误,不会出现空循环。 - 使用
AsyncWriteExt::write_all替代write:异步场景下write可能只写入部分数据,write_all会自动处理剩余数据的写入,确保数据完整发送。 - 明确处理各种返回情况:包括读取到0字节(连接关闭)和读取错误,避免静默忽略问题。
内容的提问来源于stack exchange,提问作者GNA
相关产品推荐
相关产品推荐

