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

如何终止阻塞的boost::asio串口read并让std::thread join?含3种方案问询

问题背景

由于使用同步read操作,该操作会阻塞线程t1,导致其无法与主线程join。

核心问题与最简实现方案

0. 改造为使用简单的deadline timer

通过定时取消同步read操作,让线程有机会检查退出flag,避免永久阻塞。

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

using namespace boost::asio;

io_context io;
serial_port serial_port(io, "/dev/ttyUSB0"); // 替换为实际串口路径
bool flag = true; 
uint8_t byte;

void f1()
{
    while(flag)
    {
        steady_timer timer(io);
        timer.expires_after(std::chrono::milliseconds(100)); // 每100ms检查一次flag

        // 启动定时器线程,超时则取消read
        std::thread timer_thread([&](){
            timer.wait();
            if(flag) serial_port.cancel();
        });

        try {
            read(serial_port, buffer(&byte, 1));
            timer.cancel(); // 读取成功,取消定时器
            // codeblock f1 -> 处理读取到的byte
            std::cout << "Read byte: " << static_cast<int>(byte) << std::endl;
        } catch (const boost::system::system_error& e) {
            // 忽略操作取消的错误,其他错误可按需处理
            if(e.code() != error::operation_aborted) {
                std::cerr << "Read error: " << e.what() << std::endl;
            }
        }

        timer_thread.join();
    }
}

int main()
{
    serial_port.set_option(serial_port_base::baud_rate(9600));
    
    std::thread t1(f1); 
    
    // codeblock m1 -> 执行主线程操作
    std::this_thread::sleep_for(std::chrono::seconds(2));

    flag = false ; 

    // codeblock m2 -> 执行更多主线程操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
   
    t1.join(); 

    // codeblock m3 -> 执行后续操作
    std::cout << "Main thread finished" << std::endl;

    return 0;
}

1. 最简方式使用async操作

利用Boost.Asio异步read机制,结合io_context的事件循环,避免手动线程阻塞管理。

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

using namespace boost::asio;

io_context io;
serial_port serial_port(io, "/dev/ttyUSB0");
bool flag = true; 
uint8_t byte;

void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
    if(!ec && flag) {
        // codeblock f1 -> 处理读取到的byte
        std::cout << "Read byte: " << static_cast<int>(byte) << std::endl;
        // 继续发起下一次异步read
        async_read(serial_port, buffer(&byte, 1), handle_read);
    }
}

void f1()
{
    async_read(serial_port, buffer(&byte, 1), handle_read);
    io.run(); // 启动异步事件循环
}

int main()
{
    serial_port.set_option(serial_port_base::baud_rate(9600));
    
    std::thread t1(f1); 
    
    // codeblock m1 -> 执行主线程操作
    std::this_thread::sleep_for(std::chrono::seconds(2));

    flag = false ; 
    serial_port.cancel(); // 取消当前异步read,让io_context退出循环

    // codeblock m2 -> 执行更多主线程操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
   
    t1.join(); 

    // codeblock m3 -> 执行后续操作
    std::cout << "Main thread finished" << std::endl;

    return 0;
}

2. 在codeblock m2中模拟串口输入解除阻塞

通过虚拟串口对(如Linux的tty0tty、Windows的虚拟串口工具),主线程向配对串口写入数据,解除t1的read阻塞。

前置准备(Linux)

  1. 加载tty0tty模块:sudo modprobe tty0tty
  2. 生成虚拟串口对/dev/tnt0和/dev/tnt1
#include <boost/asio.hpp>
#include <thread>
#include <iostream>
#include <fstream>

using namespace boost::asio;

io_context io;
serial_port serial_port(io, "/dev/tnt0"); // 读取端虚拟串口
bool flag = true; 
uint8_t byte;

void f1()
{
    while(flag)
    {
        try {
            read(serial_port, buffer(&byte, 1));
            // codeblock f1 -> 处理读取到的byte
            std::cout << "Read byte: " << static_cast<int>(byte) << std::endl;
        } catch (const boost::system::system_error& e) {
            std::cerr << "Read error: " << e.what() << std::endl;
            break;
        }
    }
}

int main()
{
    serial_port.set_option(serial_port_base::baud_rate(9600));
    
    std::thread t1(f1); 
    
    // codeblock m1 -> 执行主线程操作
    std::this_thread::sleep_for(std::chrono::seconds(2));

    flag = false ; 

    // codeblock m2 -> 模拟串口输入,解除read阻塞
    std::ofstream serial_sim("/dev/tnt1"); // 写入端虚拟串口
    if(serial_sim.is_open()) {
        serial_sim.put(0x00); // 写入一个字节触发read返回
        serial_sim.flush();
        serial_sim.close();
    } else {
        std::cerr << "Failed to open simulated serial port" << std::endl;
    }
   
    t1.join(); 

    // codeblock m3 -> 执行后续操作
    std::cout << "Main thread finished" << std::endl;

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:20:30