如何通过异步运行Boost子进程测试实例计数器?
解决Boost.Process async_pipe下io_service::run()阻塞无法多开子进程的问题
我太懂你这个痛点了——本来想用boost::process::child配合async_pipe实现异步启动子进程,以为io_service::run()不会卡到子进程结束,结果实际跑起来发现它直接阻塞到第一个子进程退出,根本没法同时启动多个实例来测共享内存的计数逻辑。
问题出在io_service::run()的本质上:它会一直运行,直到所有注册的异步操作都完成才会返回。而async_pipe的读写操作如果没被处理完,比如子进程还在输出数据、或者管道没被关闭,run()就会一直等,自然没法继续启动下一个子进程。
下面给你一套能在GTest里顺利多开子进程的解决方案,附代码示例:
核心思路
- 用
io_service::work让io_service保持运行状态,不会因为暂时没有异步任务就退出 - 单独开线程跑
io_service::run(),主线程专心负责启动子进程 - 异步处理管道的读写操作,避免子进程因为输出阻塞,同时不让io_service卡在未完成的管道任务上
完整代码示例
#include <boost/process.hpp> #include <boost/asio.hpp> #include <vector> #include <thread> #include <string> #include <chrono> namespace bp = boost::process; namespace asio = boost::asio; // 假设你有这个获取共享内存实例计数的函数 int get_shared_memory_instance_count(); TEST(SharedMemoryTest, MultipleInstanceCount) { asio::io_service io; // 创建work对象,确保io_service不会因为无任务而退出 asio::io_service::work work_guard(io); // 启动单独线程运行io_service,主线程可以继续操作 std::thread io_thread([&io]() { io.run(); }); std::vector<bp::child> child_processes; const int instance_count = 5; // 要启动的子进程数量 for (int i = 0; i < instance_count; ++i) { // 为每个子进程创建独立的async_pipe asio::async_pipe stdout_pipe(io); asio::async_pipe stderr_pipe(io); // 异步读取stdout,避免管道阻塞子进程,同时让io_service不挂起 asio::async_read(stdout_pipe, asio::dynamic_buffer(std::string()), [&stdout_pipe](const boost::system::error_code& ec, std::size_t) { if (!ec) stdout_pipe.close(); }); // 同样异步读取stderr asio::async_read(stderr_pipe, asio::dynamic_buffer(std::string()), [&stderr_pipe](const boost::system::error_code& ec, std::size_t) { if (!ec) stderr_pipe.close(); }); // 启动子进程,不等待它结束 child_processes.emplace_back( "./your_test_executable", // 你的目标可执行文件路径 bp::std_out > stdout_pipe, bp::std_err > stderr_pipe, io ); } // 等待所有子进程完成初始化,再检查共享内存计数 std::this_thread::sleep_for(std::chrono::seconds(1)); ASSERT_EQ(get_shared_memory_instance_count(), instance_count); // 等待所有子进程退出 for (auto& child : child_processes) { if (child.running()) { child.wait(); } } // 清理io_service和线程 work_guard.reset(); // 移除work对象,让io_service可以退出run() io_thread.join(); }
关键细节说明
io_service::work的作用:如果没有它,当所有异步读取操作暂时完成时,io_service::run()会直接返回,导致io线程退出,后续子进程的管道操作会报错。加了work对象后,io_service会一直保持运行状态,直到我们主动调用work_guard.reset()。- 异步读取管道:一定要异步处理子进程的输出,不然子进程会因为管道缓冲区满而阻塞,同时io_service也会因为未完成的管道操作而卡住
run()。 - 子进程生命周期管理:把所有
child对象存在vector里,方便统一等待退出,避免内存泄漏或者僵尸进程。
这样调整后,你就能在GTest里顺利启动多个子进程,测试共享内存的实例计数逻辑了。
内容的提问来源于stack exchange,提问作者Chrysmac
相关产品推荐
相关产品推荐

