关于QProcess子进程导致的当前进程内存持续升高问题

背景

程序A使用QProcess启动程序B,并遍历当前系统里的窗口句柄,寻找到B的窗口并嵌入A的窗口中

void TestUnit::init() {
    connect(mpProcess, &QProcess::started, this, [this]() {
        mpWidget = findProcessWidget(mpProcess);
        mbStatus = true;
        emit tessInited();
	});

    auto tessPath = QCoreApplication::applicationDirPath() + "/xxx.exe";
    mpProcess->start(tessPath);
}

现象

随着TESS NG仿真任务的不断进行,当前程序的内存持续升高,且步幅越来越大,速度越来越快

原因

enum QProcess::ProcessChannelMode

该枚举描述了进程输出通道模式QProcess。将这些值之一传递给setProcessChannelMode() 设置当前读取通道模式。

持续的

价值

描述

QProcess::SeparateChannels

0

QProcess 管理运行进程的输出,将标准输出和标准错误数据保存在不同的内部缓冲区中。通过调用 setReadChannel() 可以选择 QProcess 当前的读取通道。这是 QProcess 的默认通道模式。

QProcess::MergedChannels

1

QProcess 会将运行进程的输出合并到标准输出通道 (stdout) 中。标准错误通道(stderr)不会接收任何数据。运行进程的标准输出和标准错误数据会交错排列。对于分离进程,运行进程的合并输出将转发给主进程。

QProcess::ForwardedChannels

2

QProcess 会将运行进程的输出转发给主进程。子进程写入其标准输出和标准错误的任何内容都将写入主进程的标准输出和标准错误。

QProcess::ForwardedErrorChannel

4

QProcess 管理运行进程的标准输出,但会将标准错误转发给主进程。这反映了命令行工具作为过滤器的典型用途,即标准输出被重定向到另一个进程或文件,而标准错误被打印到控制台,用于诊断目的。(此值在 Qt 5.2 中引入)

QProcess::ForwardedOutputChannel

3

是 ForwardedErrorChannel 的补充。(该值在 Qt 5.2 中引入)。

注意: Windows 会有意抑制从纯图形用户界面应用程序到继承控制台的输出。这不适用于重定向到文件或管道的输出。要在控制台上转发纯图形用户界面应用程序的输出,必须使用 SeparateChannels,并通过读取输出并将其写入适当的输出通道来自行完成转发。

当调用QProcess的start()函数启动子进程时,QProcess会为子进程创建三个管道:标准输入、标准输出和标准错误。这些管道将被用于子进程与主进程之间的通信。在使用管道通信时,需要确保适当地处理子进程的输出和错误信息。否则,如果子进程输出过多的数据或发生错误,可能会导致管道阻塞,从而导致主进程挂起或崩溃。

解决办法

void TestUnit::init() {
    connect(mpProcess, &QProcess::started, this, [this]() {
        mpWidget = findProcessWidget(mpProcess);
        mbStatus = true;
        emit tessInited();
    });

    auto tessPath = QCoreApplication::applicationDirPath() + "/xxx.exe";
    mpProcess->start(tessPath, QProcess::Truncate);

    QObject::connect(mpProcess, &QProcess::readyReadStandardOutput, [&]() {
        auto output = mpProcess->readAllStandardOutput();
    });
    QObject::connect(mpProcess, &QProcess::readyReadStandardError, [&]() {
        auto errorOutput = mpProcess->readAllStandardError();
    });
}