创建线程后调用BOOST_LOG_SEV导致async_read_some异常的问题咨询
解决Boost.Asio串口异步读取加日志后的异常问题
这问题我之前帮同事排查过几乎一模一样的情况,大概率是线程生命周期或者日志线程安全的锅,咱们一步步拆解:
最可能的原因:IO服务线程提前退出
你现在的代码顺序是先启动io_service的线程,再发起async_read_some异步操作——这里有个极易踩的坑:如果io_service.run()调用时没有待处理的异步任务,它会立刻返回,线程直接结束。
为什么加日志后才出问题?因为日志输出需要消耗少量时间,让IO线程有足够的时间完全退出,等你调用async_read_some时,已经没有线程在处理IO服务的任务了,自然会表现出“异常”(比如回调永远不触发、操作超时)。
对应解决方案
有两种靠谱的处理方式:
- 调整顺序:先发起异步操作,再启动IO线程
先调用port_->async_read_some(...)把任务塞进IO服务,再启动线程执行io_service.run(),这样线程启动后就有任务可处理,不会直接退出。 - 用
io_service::work保持线程存活
创建一个work对象绑定到你的IO服务上,只要这个对象存在,io_service.run()就会一直阻塞(即使没有任务),直到你主动停止IO服务或者销毁work对象:// 在启动线程前创建work对象(注意生命周期要覆盖IO线程的运行时间) boost::asio::io_service::work keep_alive(io_service_); boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service_));
第二个可能:Boost.Log的线程安全或初始化问题
Boost.Log内部依赖线程本地存储(TLS),如果初始化时机不对或者sink没有配置线程安全,跨线程的日志操作可能会干扰IO服务的正常运行:
- 确保全局日志初始化在所有线程启动前完成:比如在
main函数最开始就初始化日志核心、sink和日志器,不要等到线程启动后才初始化。 - 配置日志sink为线程安全:如果用了文件或自定义sink,需要明确开启线程安全选项。比如文件sink的配置:
auto file_backend = boost::log::sinks::text_file_backend::create(); file_backend->set_file_name_pattern("serial_log_%Y%m%d.log"); // 开启线程安全模式 file_backend->set_thread_safe(true); auto file_sink = boost::log::sinks::synchronous_sink<decltype(file_backend)>::create(file_backend); boost::log::core::get()->add_sink(file_sink); - 检查日志器获取的线程安全性:
MBLog::get()返回的日志器实例要确保是线程安全的,不会在多线程环境下返回无效指针或未初始化的对象。
其他排查方向
- 检查缓冲区生命周期:确保
read_buf_raw_在async_read_some的回调函数执行完成前一直处于有效状态——异步操作会持有缓冲区的引用,如果缓冲区提前销毁,会触发未定义行为(崩溃、数据乱码等)。 - 调试IO线程状态:给IO线程加个收尾日志,看看它是否真的提前退出了:
如果输出的boost::thread t([&](){ std::size_t processed_tasks = io_service_.run(); BOOST_LOG_SEV(MBLog::get(), boost::log::trivial::info) << "IO服务线程退出,共处理了" << processed_tasks << "个任务"; });processed_tasks是0,就说明线程启动时没有任务,直接退出了,这就坐实了第一个原因。
内容的提问来源于stack exchange,提问作者Patryk Koziel
相关产品推荐
相关产品推荐

