在一个进程中使用多个io_context会导致超时

问题描述 投票:0回答:1

我有一个通过使用

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 "";
  }
}

谢谢

c++ boost
1个回答
0
投票

代码中有很多错误,这告诉我们该代码不是不是您的实际代码。

根据经验我可以猜出问题是什么。

您可能正在重复使用

io_context
。当子进程退出(通过正常或异常终止)时,您冗余地
ioc.stop()
的事实告诉我,您没有意识到
io_context.run()
当它耗尽工作时将 always 返回(停止)。

当服务耗尽工作时,它总是从

run()
立即返回,直到调用
restart()
docs

run()
函数正常退出意味着 io_context 对象已停止(
stopped()
函数返回 true)。后续调用
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);

事实上,您可以简单地提供

boost::system::error_code
特性并依赖于构造函数重载 (3)
,而不是依赖 
is_error_code_enum 提供的转换,这正是我刚才描述的:

ec = asio::error::timed_out;

也许以类似的方式,如果您使用

boost::filesystem::path

,您将受益于 
boost::process
 的直接支持,因此您不必使用 
path::string
path::generic
path::native
 从类型系统中触底。事实上,我认为
native()
在这里是正确的。

如果您接受持续时间类型,则可以避免不必要的舍入到秒。

建议将子进程包装在进程组中,这样当您终止子进程时就不会留下孙子进程。

这是我的看法:

住在Coliru

#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: ""

    
© www.soinside.com 2019 - 2024. All rights reserved.