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

Linux下如何正确中断boost::asio::serial_port的同步read操作?

问题描述

现有一段实现串口打开、启动同步读取线程并尝试停止线程的代码,在Windows下运行正常,但在Linux下调用port.cancel()无法中断同步读取,程序永久挂起。

示例代码

#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <chrono>

void readThread(boost::asio::serial_port* port)
{
    static const size_t maxBytesToRead = 256;
    unsigned char buffer[maxBytesToRead];
    size_t read = 0;
    while (true)
    {
        try
        {
            std::cout << "readThread reading" << std::endl;
            read = boost::asio::read(*port,
                        boost::asio::buffer(buffer, maxBytesToRead));
        }
        catch (...)
        {
            std::cout << "exception in readThread, about to stop" << std::endl;
            return;
        }
    }
}

int main( int argc, char* argv[] )
{
    boost::asio::io_service service;
    boost::asio::serial_port port(service);

    //port.open("/dev/ttyACM0");
    port.open("COM9");
    port.set_option(boost::asio::serial_port_base::baud_rate(115200));
    port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
    port.set_option(boost::asio::serial_port_base::character_size(8));
    port.set_option(
            boost::asio::serial_port_base::flow_control(
                boost::asio::serial_port_base::flow_control::hardware));

    std::thread thrd(&readThread, &port);

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    std::cout << "Calling cancel()" << std::endl; 
    port.cancel();
    std::cout << "Called cancel()" << std::endl;

    thrd.join();

    std::cout << "Exiting, OK" << std::endl;

    return 0;
}

运行差异

  • Windows下输出:
readThread reading
Calling cancel()
Called cancel()
exception in readThread, about to stop
Exiting, OK
  • Linux下输出:程序挂起,仅输出以下内容后无响应
readThread reading
Calling cancel()
Called cancel()

提问

在尽量不改动代码结构、不切换到异步读取的前提下,正确中断该同步读取的策略是什么?


解决方案

在Linux系统中,Boost.Asio的serial_port::cancel()对同步读取操作的中断支持存在限制,因为同步IO操作不会主动响应cancel请求。可以通过以下两种兼容策略解决:

策略一:关闭串口触发异常

在主线程中调用port.cancel()后,立即关闭串口。关闭操作会中断正在进行的同步读取,触发boost::system::system_error异常,线程可以通过现有catch块捕获并退出。修改主线程相关代码:

std::cout << "Calling cancel()" << std::endl; 
port.cancel();
port.close(); // 添加这行代码
std::cout << "Called cancel()" << std::endl;

这种方式不需要改动读取线程的逻辑,仅在主线程中增加关闭操作,完全符合“尽量不改动代码结构”的要求。

策略二:设置读取超时

利用Boost.Asio的deadline_timer为同步读取设置超时,结合线程退出标志,让读取线程主动检查是否需要退出。具体步骤:

  1. 给读取线程添加一个原子类型的退出标志(比如std::atomic<bool>& shouldExit)
  2. 在主线程中触发退出标志并调用cancel()
  3. 读取线程中使用read_some结合超时机制,定期检查退出标志

不过这种方式需要对读取线程做少量修改,但依然保留同步读取的核心逻辑,不需要切换到异步IO。

优先推荐策略一,因为改动最小,直接利用关闭串口的操作中断同步读取,和Windows下的异常捕获逻辑兼容,线程会通过现有catch块捕获异常并退出。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 21:10:28