C++ `boost::asio::io_context`with`boost::process::async_pipe`:有没有可靠的运行方法?

C++ `boost::asio::io_context`with`boost::process::async_pipe`:有没有可靠的运行方法?,c++,asynchronous,boost,boost-asio,boost-process,C++,Asynchronous,Boost,Boost Asio,Boost Process,有了Windows API,我们可以使用管道转发进程输出和错误流,这样我们就可以读取进程输出而无需任何临时文件。与此相反: std::system("my_command.exe > out.tmp"); 我们可以更快地生成大量被遗忘的临时文件(例如在系统崩溃时),而不会有风险 Linux具有类似的功能。然而,为每个操作系统实现特定于操作系统的代码既耗时又复杂,所以使用一些可移植的解决方案似乎是个好主意 boost::process声称就是这样的解决方案。然而,它从根本上是不可靠的。请参

有了Windows API,我们可以使用管道转发进程输出和错误流,这样我们就可以读取进程输出而无需任何临时文件。与此相反:

std::system("my_command.exe > out.tmp");
我们可以更快地生成大量被遗忘的临时文件(例如在系统崩溃时),而不会有风险

Linux具有类似的功能。然而,为每个操作系统实现特定于操作系统的代码既耗时又复杂,所以使用一些可移植的解决方案似乎是个好主意

boost::process
声称就是这样的解决方案。然而,它从根本上是不可靠的。请参阅以下示例程序:

#include <fstream>
#include <iostream>
#include <memory>
#include <vector>

#include <boost/asio.hpp>
#include <boost/process.hpp>

void ReadPipe(boost::process::async_pipe& pipe, char* output_buffer, size_t output_buffer_size, boost::asio::io_context::strand& executor, std::ofstream& output_saver)
{
    namespace io = boost::asio;
    using namespace std;
    io::async_read(
        pipe,
        io::buffer(
            output_buffer,
            output_buffer_size
        ),
        io::bind_executor(executor, [&pipe, output_buffer, output_buffer_size, &executor, &output_saver](const boost::system::error_code& error, std::size_t bytes_transferred) mutable
            {
                // Save transferred data
                if (bytes_transferred)
                    output_saver.write(output_buffer, bytes_transferred);
                // Handle error
                if (error)
                {
                    if (error.value() == boost::asio::error::basic_errors::broken_pipe)
                        cout << "Child standard output is broken, so the process is most probably exited." << endl;
                    else
                        cout << "Child standard output read error occurred. " << boost::system::system_error(error).what() << endl;
                }
                else
                {
                    //this_thread::sleep_for(chrono::milliseconds(50));
                    ReadPipe(pipe, output_buffer, output_buffer_size, executor, output_saver);
                }
            })
    );
}

int main(void)
{
    namespace io = boost::asio;
    namespace bp = boost::process;
    using namespace std;
    // Initialize
    io::io_context asio_context;
    io::io_context::strand executor(asio_context);
    bp::async_pipe process_out(asio_context);
    char buffer[65535];
    constexpr const size_t buffer_size = sizeof(buffer);
    ofstream output_saver(R"__(c:\screen.png)__", ios_base::out | ios_base::binary | ios_base::trunc);
    // Schedule to read standard output
    ReadPipe(process_out, buffer, buffer_size, executor, output_saver);
    // Run child
    bp::child process(
        bp::search_path("adb"),
        bp::args({ "exec-out", "screencap", "-p" }),
        bp::std_in.close(),
        bp::std_out > process_out,
        bp::std_err > process_out
    );
    asio_context.run();
    process.wait();
    output_saver.close();
    // Finish
    return 0;
}
管道读取操作变得完全不可靠。程序只读取部分数据(大小不可预测)

所以,即使只有50毫秒的短暂延迟也会迫使异步管道的boost实现失败

这不是正常情况。如果CPU使用率接近100%(即我们在高负载服务器上),该怎么办?如果线程运行可能在50毫秒或更短时间内执行的其他ASIO作业,该怎么办?所以,它只是简单地复制了基本的boost ASIO错误的实现:异步管道在您开始阅读它时不能容忍任何延迟;您必须在收到数据后立即再次调用
async\u read
,否则您可能会丢失数据

实际上,当我使用同一ASIO上下文运行多个作业时(不仅仅是一个读取流程标准输出的
async_read
),在尝试读取1M或更多数据时,
async_pipe
会失败50%


如果ASIO上下文以运行其他作业所需的非常小的延迟运行
async\u read
,有人知道如何使
async\u管道
可靠且不中断连接的解决方法吗?

您可能想分享程序的实际输出,因此,我们知道管道上可能存在的错误。您可能希望共享程序的实际输出,因此我们知道管道上可能存在的错误。
this_thread::sleep_for(chrono::milliseconds(50));