使用 BOOST 进程在单独的线程中读取子进程标准输出
Posted
技术标签:
【中文标题】使用 BOOST 进程在单独的线程中读取子进程标准输出【英文标题】:Read child process stdout in a separate thread with BOOST process 【发布时间】:2018-08-17 06:42:10 【问题描述】:我有一个主程序,它使用 boost 进程库来生成一个打印的子进程
Hello World !
每 5 秒在其标准输出上。
我想在主进程中读取/监视子进程的标准输出,当它可用时,以及在主程序中执行其他操作。
我已经尝试了boost asynchronous IO
(http://www.boost.org/doc/libs/1_66_0/doc/html/boost_process/tutorial.html) 的示例,但所有这些似乎都会阻塞主程序,直到子进程退出。
我们是否需要在单独的线程中读取孩子的标准输出?有人可以提供一个例子,主程序可以同时做其他事情,而不是阻止来自孩子的标准输出吗?
【问题讨论】:
请出示您的代码。您提供的链接中的bp::child
应该可以解决问题。
示例代码块(我认为)由于对boost::asio::io_service::run()
的调用。根据您的需要,您可能希望将其替换为对boost::asio::io_service::poll
的“间歇”调用。但是,如果没有看到您的代码(或者确切地知道为什么阻塞是一个问题),就很难进一步评论。
@G.M.如果您使用异步调用链,则本着异步操作的精神,无需交错事件循环。
我经常在这里回答这类问题。我喜欢在答案中提供不同的想法,所以比较和选择:***.com/questions/tagged/…
@sehe 点了。在发表评论之前,我应该在我的 boost
上“刷一下”。
【参考方案1】:
我已经尝试了 boost 异步 IO (http://www.boost.org/doc/libs/1_66_0/doc/html/boost_process/tutorial.html) 的示例,但所有这些似乎都会阻塞主程序,直到子进程退出。
再看一遍。 Asynchronous I/O 下的所有示例应该可以帮助您选择适合您的方法。
我们是否需要在单独的线程中读取孩子的标准输出?有人可以提供一个例子,主程序可以同时做其他事情,而不是阻止来自孩子的标准输出吗?
不,你不需要。虽然您可以并且根据您想要达到的目标,这可能是最简单的事情。
同步
你没有告诉我们你想做什么,所以我们假设你只想打印输出:
Live On Coliru:
bp::child c("/bin/bash", std::vector<std::string> "-c", "for a in 1..10; do sleep 2; echo 'Hello World !'; done" );
c.wait();
这是同步的,所以你不能同时工作
使用阅读器线程
就像这样:
Live On Coliru:
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
namespace bp = boost::process;
int main()
bp::ipstream output;
std::thread reader([&output]
std::string line;
while (std::getline(output, line))
std::cout << "Received: '" << line << "'" << std::endl;
);
bp::child c("/bin/bash",
std::vector<std::string> "-c", "for a in 1..10; do sleep 2; echo 'Hello World ('$a')!'; done" ,
bp::std_out > output);
while (c.running())
std::this_thread::sleep_for(std::chrono::milliseconds(2793));
std::cout << "(main thread working)" << std::endl;
std::cout << "(done)" << std::endl;
c.wait();
output.pipe().close();
reader.join();
打印(Live On Coliru):
Received: 'Hello World (1)!'
(main thread working)
Received: 'Hello World (2)!'
(main thread working)
Received: 'Hello World (3)!'
Received: 'Hello World (4)!'
(main thread working)
Received: 'Hello World (5)!'
(main thread working)
Received: 'Hello World (6)!'
(main thread working)
Received: 'Hello World (7)!'
Received: 'Hello World (8)!'
(main thread working)
Received: 'Hello World (9)!'
(main thread working)
Received: 'Hello World (10)!'
(main thread working)
(done)
异步 IO
不使用线程(嗯,只是主线程),可能看起来像:
Live On Coliru
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>
#include <iomanip>
namespace bp = boost::process;
struct OtherWork
using clock = std::chrono::high_resolution_clock;
OtherWork(boost::asio::io_context& io) : timer(io)
void start()
timer.expires_at(clock::time_point::max());
loop();
void stop()
timer.expires_at(clock::time_point::min());
private:
void loop()
if (timer.expires_at() == clock::time_point::min())
std::cout << "(done)" << std::endl;
return;
timer.expires_from_now(std::chrono::milliseconds(2793));
timer.async_wait([=](boost::system::error_code ec)
if (!ec)
std::cout << "(other work in progress)" << std::endl;
start();
else
std::cout << "(" << ec.message() << ")" << std::endl;
);
boost::asio::high_resolution_timer timer;
;
int main()
boost::asio::io_context io;
bp::async_pipe output(io);
OtherWork mainworkio;
bp::child c("/bin/bash", std::vector<std::string> "-c", "for a in 1..10; do sleep 2; echo 'Hello World ('$a')!'; done" ,
bp::std_out > output, io, bp::on_exit([&mainwork,&output](auto...)
output.close();
mainwork.stop();
));
std::function<void()> readloop = [&,buffer=std::array<char, 32>]() mutable
output.async_read_some(bp::buffer(buffer), [&](boost::system::error_code ec, size_t transferred)
if (transferred)
std::cout << "Received: '";
while (transferred && buffer[transferred-1] == '\n') // strip newline(s)
--transferred;
std::cout.write(buffer.data(), transferred);
std::cout << "'" << std::endl;
if (ec)
std::cout << "Output pipe: " << ec.message() << std::endl;
else
readloop();
);
;
mainwork.start();
readloop();
io.run();
打印Live On Coliru
Received: 'Hello World (1)!'
(other work in progress)
Received: 'Hello World (2)!'
(other work in progress)
Received: 'Hello World (3)!'
Received: 'Hello World (4)!'
(other work in progress)
Received: 'Hello World (5)!'
(other work in progress)
Received: 'Hello World (6)!'
(other work in progress)
Received: 'Hello World (7)!'
Received: 'Hello World (8)!'
(other work in progress)
Received: 'Hello World (9)!'
(other work in progress)
Received: 'Hello World (10)!'
Output pipe: End of file
Child exited with code=0(Success)
(Operation canceled)
【讨论】:
在阅读器线程示例中,我无法了解阅读器线程的打印方式,因为在生成子进程时,子进程的标准输出未重定向到 ipstream 对象。将此更改为bp::child c("/bin/bash", std::vector<std::string> "-c", "for a in 1..10; do sleep 2; echo 'Hello World ($a)!'; done" , bp::std_out > output);
会返回以下输出:Received: 'Hello World (1)!' (main thread working) Received: 'Hello World (2)!' (main thread working) Received: 'Hello World (3)!' execution expired
你是对的。我有一些副本和过去的错误。现在修好了。 (见live)“Execution expired”是由于 Coliru 的时间限制以上是关于使用 BOOST 进程在单独的线程中读取子进程标准输出的主要内容,如果未能解决你的问题,请参考以下文章
使用 boost::process 读取 cout 并写入进程的 cin
Python - 在单独的子进程或线程中运行 Autobahn|Python asyncio websocket 服务器