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

Rust串口通信多线程写入冻结问题求助

Rust串口双线程读写阻塞问题解决

问题根源

serialport crate的try_clone()在Windows等平台并未创建完全独立的串口句柄,底层共享同步锁,导致读线程阻塞时写线程无法获取锁,进而冻结。另外,默认无超时配置会让读线程无限期阻塞,加剧问题。

修复方案

方案1:配置超时+优化读逻辑

为串口设置读写超时,改用批量读取替代bytes()遍历,避免线程长期占用锁:

use std::{
    io::{stdin, stdout, Read, Write},
    thread,
    time::Duration,
};

fn main() {
    // 打开串口并设置读超时
    let mut port = serialport::new("COM5", 9600)
        .timeout(Duration::from_millis(100))
        .open()
        .unwrap();
    // 显式设置写超时
    port.set_write_timeout(Duration::from_millis(100)).unwrap();

    let mut r_port = port.try_clone().unwrap();

    // 读线程:批量读取,超时则循环重试
    thread::spawn(move || {
        let mut read_buf = [0u8; 128];
        loop {
            match r_port.read(&mut read_buf) {
                Ok(n) if n > 0 => {
                    print!("{}", String::from_utf8_lossy(&read_buf[..n]));
                    stdout().flush().unwrap();
                }
                Ok(_) => {} // 无数据超时,继续循环
                Err(e) => {
                    eprintln!("读串口错误: {}", e);
                    break;
                }
            }
        }
    });

    // 主线程处理用户输入发送
    loop {
        let mut input = String::new();
        stdin().read_line(&mut input).unwrap();
        match port.write(input.as_bytes()) {
            Ok(_) => {},
            Err(e) => eprintln!("写串口错误: {}", e),
        }
    }
}

方案2:用通道单线程处理串口

如果克隆句柄问题无法解决,改用主线程统一处理串口读写,通过mpsc通道转发用户输入:

use std::{
    io::{stdin, stdout, Read, Write},
    sync::mpsc::{self, Receiver, Sender},
    thread,
    time::Duration,
};

fn main() {
    let mut port = serialport::new("COM5", 9600)
        .timeout(Duration::from_millis(50))
        .open()
        .unwrap();

    // 创建通道,用于输入线程向主线程发送数据
    let (tx, rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = mpsc::channel();

    // 用户输入线程:只负责读取控制台输入并发送到通道
    thread::spawn(move || {
        loop {
            let mut input = String::new();
            stdin().read_line(&mut input).unwrap();
            tx.send(input.as_bytes().to_vec()).unwrap();
        }
    });

    // 主线程:同时处理串口读和通道数据发送
    let mut read_buf = [0u8; 128];
    loop {
        // 优先读取串口数据
        match port.read(&mut read_buf) {
            Ok(n) if n > 0 => {
                print!("{}", String::from_utf8_lossy(&read_buf[..n]));
                stdout().flush().unwrap();
            }
            _ => {}
        }

        // 尝试接收通道中的输入数据并发送
        if let Ok(data) = rx.try_recv() {
            port.write(&data).unwrap();
        }
    }
}

额外检查项

  • 确认Arduino代码使用Serial.available()判断后再读取,避免串口缓冲区阻塞。
  • Windows平台下,关闭程序后需等待几秒再重新运行,防止串口资源未释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:43:09