如何用Boost Process在独立线程读取子进程stdout且不阻塞主程序
解决Boost Process异步监控子进程stdout同时执行主程序任务的问题
嘿,我完全懂你的困扰!Boost Process的官方示例有时候确实会给人一种“异步操作也会阻塞主流程”的错觉,但其实只要调整一下事件循环的使用方式,就能轻松实现边监控子进程输出边跑主程序自己的逻辑。
核心思路
关键在于利用Boost Asio的io_context来统一调度所有异步任务——包括子进程的stdout读取,以及主程序自己的业务逻辑。这样两者就能在同一个事件循环里并发执行,互不阻塞。
完整代码示例
1. 子进程代码(每隔5秒输出内容)
先写个简单的子进程程序,模拟每5秒向stdout打印信息:
// child_process.cpp #include <iostream> #include <chrono> #include <thread> int main() { int count = 0; while (true) { std::cout << "子进程输出:第" << ++count << "次消息" << std::endl; std::cout.flush(); // 一定要刷新缓冲区,否则主进程可能读不到 std::this_thread::sleep_for(std::chrono::seconds(5)); } return 0; }
2. 主程序代码(异步监控+执行自身任务)
主程序用async_pipe建立异步管道,异步读取子进程stdout,同时在事件循环里加入自己的定时任务:
#include <boost/process.hpp> #include <boost/asio.hpp> #include <iostream> #include <chrono> namespace bp = boost::process; namespace asio = boost::asio; // 异步读取子进程stdout的回调函数 void read_stdout(asio::async_pipe& pipe, char* buffer, std::size_t buffer_size) { pipe.async_read_some( asio::buffer(buffer, buffer_size), [&](const boost::system::error_code& ec, std::size_t bytes_read) { if (!ec) { // 处理读取到的内容 std::cout << "[主进程收到]:" << std::string(buffer, bytes_read); // 继续发起下一次异步读取,保持持续监控 read_stdout(pipe, buffer, buffer_size); } else if (ec != asio::error::eof) { std::cerr << "读取子进程输出出错:" << ec.message() << std::endl; } } ); } // 主程序自己的任务:每隔2秒打印一次状态 void main_task(asio::io_context& io_context) { std::cout << "[主程序]:正在执行自身任务..." << std::endl; // 2秒后再次执行该任务,形成循环 io_context.post([&]() { asio::steady_timer timer(io_context, std::chrono::seconds(2)); timer.async_wait([&](const boost::system::error_code&) { main_task(io_context); }); }); } int main() { asio::io_context io_context; bp::async_pipe stdout_pipe(io_context); // 启动子进程,将其stdout重定向到异步管道 bp::child child_process( "./child_process", // 子进程可执行文件路径 bp::std_out > stdout_pipe, io_context ); // 初始化缓冲区,开始异步读取子进程stdout char buffer[1024]; read_stdout(stdout_pipe, buffer, sizeof(buffer)); // 启动主程序自己的任务 main_task(io_context); // 运行事件循环,调度所有异步任务 io_context.run(); return 0; }
关键细节说明
async_pipe的使用:这是实现异步IO的核心,它替代了普通的管道,支持Boost Asio的异步操作接口。- 循环异步读取:在
read_stdout的回调里,必须再次调用read_stdout发起下一次读取,否则只能获取到子进程的一次输出。 - 主程序任务的调度:通过
io_context::post和定时器,把主程序的业务逻辑加入事件循环,这样它会和子进程的读取任务交替执行,不会阻塞。 - 缓冲区刷新:子进程里一定要用
std::cout.flush(),否则输出会被缓冲,主进程无法及时收到内容。
运行效果
启动主程序后,你会看到:
[主程序]:正在执行自身任务... [主进程收到]:子进程输出:第1次消息 [主程序]:正在执行自身任务... [主程序]:正在执行自身任务... [主进程收到]:子进程输出:第2次消息 ...
主程序的任务每2秒执行一次,子进程的输出每5秒被主进程捕获一次,两者完全并行,互不干扰。
内容的提问来源于stack exchange,提问作者sbunny




