我看到最引人注目的概念问题是:
Process are asynchronous, no need to add a thread to run them.¹
You prematurely close the pipe:
mService.run()
mStream.pipe().close()
Run is not "blocking" in the sense that it will not wait for the child to exit. You could use wait
to achieve that. Other than that, you can just remove the close()
call.
With the close means you will lose all or part of the output. You might not see any of the output if the child process takes a while before it outputs the first data.
You are accessing the mStream
from multiple threads without synchronization. This invokes Undefined Behaviour because it opens a Data Race.
In this case you can remove the immediate problem by removing the mStream.close()
call mentioned before, but you must take care to start the reader-thread only after the child
has been initialized.
Strictly speaking the same caution should be taken for std::cout
.
You are passing the io_service
reference, but it's not being used. Just dropping it seems like a good idea.
The destructor of MyProcess
needs to detach or join the threads. To prevent Zombies, it needs to detach or reap the child pid too.
In combination with the lifetime of mStream
detaching the reader thread is not really an option, as mStream
is being used from the thread.
让我们先解决第一个问题,之后我将建议显示更多的简化内容,这些内容在您的示例范围内有意义。
首要修复
我使用了一个简单的bash命令来模拟生成1000行ping
的命令:
在Coliru上实时演示
#include <boost/process.hpp>
#include <thread>
#include <iostream>
namespace bp = boost::process;
class MyProcess {
bp::ipstream mStream;
bp::child mChild;
std::thread mReaderThread;
public:
~MyProcess();
void launch();
};
void MyProcess::launch() {
mChild = bp::child("/bin/bash", std::vector<std::string> {"-c", "yes ping | head -n 1000" }, bp::std_out > mStream);
mReaderThread = std::thread([&]() {
std::string line;
while (getline(mStream, line)) {
std::cout << line << std::endl;
}
});
}
MyProcess::~MyProcess() {
if (mReaderThread.joinable()) mReaderThread.join();
if (mChild.running()) mChild.wait();
}
class MyGui {
MyProcess _process;
public:
void launchProcess();
};
void MyGui::launchProcess() {
_process.launch();
}
int main() {
MyGui gui;
gui.launchProcess();
}
简化!
在当前模型中,线程没有发挥它的作用。
如果你使用异步IO的io_service
,甚至可以通过在GUI事件循环²中轮询服务来摆脱整个线程。
如果你打算这样做,而且由于子进程自然地异步执行³,你可以简单地执行以下操作:
在Coliru上实时运行
#include <boost/process.hpp>
#include <thread>
#include <iostream>
std::thread launch(std::string const& command, std::vector<std::string> args = {}) {
namespace bp = boost::process;
return std::thread([=] {
bp::ipstream stream;
bp::child c(command, args, bp::std_out > stream);
std::string line;
while (getline(stream, line)) {
std::cout << line << std::endl;
}
c.wait();
});
}
这个演示与之前的输出完全相同。
¹ 实际上,添加线程可能会导致使用fork
时出现问题。
² 或者类似的空闲滴答声的想法。Qt有一个现成的集成方案(如何在像Qt4或GTK这样的GUI框架中集成Boost.Asio主循环)。
³ 在Boost Process支持的所有平台上。