如何终止阻塞的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)
- 加载tty0tty模块:
sudo modprobe tty0tty - 生成虚拟串口对
/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
相关产品推荐
相关产品推荐

