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为同步读取设置超时,结合线程退出标志,让读取线程主动检查是否需要退出。具体步骤:
- 给读取线程添加一个原子类型的退出标志(比如
std::atomic<bool>& shouldExit) - 在主线程中触发退出标志并调用
cancel() - 读取线程中使用
read_some结合超时机制,定期检查退出标志
不过这种方式需要对读取线程做少量修改,但依然保留同步读取的核心逻辑,不需要切换到异步IO。
优先推荐策略一,因为改动最小,直接利用关闭串口的操作中断同步读取,和Windows下的异常捕获逻辑兼容,线程会通过现有catch块捕获异常并退出。
内容的提问来源于stack exchange,提问作者jpo38
相关产品推荐
相关产品推荐

