我有一个通过使用
boost::process::child
调用 io_context
定义来运行进程的函数。在子创建之后,我调用 io_context.run_for(timeout)
,当它完成时,我知道超时已经过去或进程成功终止。
进程标准输出从类型
std::future<std::string>
转到变量,因此我在 io_context 完成运行后检查数据是否已准备好。
第一次通话很顺利。然而,经过几次成功的尝试后,我得到了下一个调用一些奇怪的行为,以便 io_context.run_for 迅速完成超时值(尽管超时肯定还没有达到) - 这个问题只在 macOS 中发生在我身上(至少根据我不是的人
我想知道创建多个 io_context 来并行或串行运行是否是一种预期的方法?
这是我的代码:
std::string ExecuteProcess(const std::filesystem::path& processPath,
const std::vector<std::string>& args,
const std::chrono::seconds& time,
std::error_code& ec) {
boost::asio::io_context ioc;
std::future<std::string> data;
std::future<std::string> err_output;
boost::process::child child(
boost::process::exe = process_path.string(),
boost::process::args = arguments, boost::process::std_out > data,
boost::process::std_err > err_output, ioc,
boost::process::on_exit(
[&ec, &ioc, error_code) {
// do stuff with ec
ioc.stop();
}));
ioc.run_for(time); // this should end when process complete or after timeout
if (child.running()) {
child.terminate();
}
if (data.wait_for(0s) == std::future_status::ready) { // no need to wait here
std::string output = data.get();
return output;
} else {
ec = boost::asio::error::make_error_code(boost::asio::error::timed_out);
return "";
}
}
谢谢
代码中有很多错误,这告诉我们该代码不是不是您的实际代码。
根据经验我可以猜出问题是什么。
您可能正在重复使用
io_context
。当子进程退出(通过正常或异常终止)时,您冗余地 ioc.stop()
的事实告诉我,您没有意识到 io_context.run()
当它耗尽工作时将 always 返回(停止)。
当服务耗尽工作时,它总是从
run()
立即返回,直到调用 restart()
:docs
从
函数正常退出意味着 io_context 对象已停止(run()
函数返回 true)。后续调用stopped()
、run()
、run_one()
或poll()
将立即返回,除非之前调用过poll_one()
restart()
在你的情况下,我只是不共享上下文,就像你的代码所示
还有一些其他错综复杂的问题。例如。这确实是一个竞争条件
if (child.running())
child.terminate();
最好忽略来自
terminate
的错误。
如果您的子进程应该不接收
stdin
,最好也断开与bp::std_in.null()
的连接。
make_error_code
应该由 ADL 调用,这样您就可以获得库设计者预期的行为:
ec = make_error_code(asio::error::timed_out);
也许以类似的方式,如果您使用事实上,您可以简单地提供
特性并依赖于构造函数重载 (3)boost::system::error_code
,而不是依赖is_error_code_enum
提供的转换,这正是我刚才描述的:ec = asio::error::timed_out;
boost::filesystem::path
,您将受益于
boost::process
的直接支持,因此您不必使用
path::string
、
path::generic
或
path::native
从类型系统中触底。事实上,我认为
native()
在这里是正确的。如果您接受持续时间类型,则可以避免不必要的舍入到秒。
建议将子进程包装在进程组中,这样当您终止子进程时就不会留下孙子进程。
这是我的看法:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
using duration = std::chrono::system_clock::duration;
namespace asio = boost::asio;
using namespace std::chrono_literals;
std::string ExecuteProcess(boost::filesystem::path exe,
std::vector<std::string> args, //
duration time, //
std::error_code& ec) {
namespace bp = boost::process;
asio::io_context ioc;
std::future<std::string> data, err_output;
bp::group g;
bp::child child(exe, args, ioc, g, bp::error(ec),
bp::std_in.null(), //
bp::std_out > data, //
bp::std_err > err_output);
ioc.run_for(time);
if (std::error_code ignore; child.running())
g.terminate(ignore);
if (data.wait_for(0s) == std::future_status::ready)
return data.get();
ec = make_error_code(asio::error::timed_out);
return {};
}
int main() {
constexpr duration timeout = 20ms;
constexpr auto script = "sleep 0.0$RANDOM; echo -n 'Hello world'";
for (std::error_code ec; /**/;) {
auto s = ExecuteProcess("/usr/bin/bash", {"-c", script}, timeout, ec);
std::cout << "got " << ec.message() << ": " << quoted(s) << std::endl;
}
}
印刷
got Success: "Hello world"
got Success: "Hello world"
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: "Hello world"
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: "Hello world"
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: "Hello world"
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: "Hello world"
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: ""
got Connection timed out: "Hello world"
got Connection timed out: "Hello world"
got Connection timed out: ""