QProcess не выдает свои сигналы, когда не waitForFinished()

В следующем коде опущено waitForFinished() заставляет QProcess перестать излучать свой сигнал. Какого черта с этим не так? Это Qt Bug? (5.7). Обратите внимание, что этот код запускается параллельно с QtConcurrent run. Но это ничего не должно изменить, не так ли? Afaik отправляет сигналы в другие потоки нормально, хотя они будут поставлены в очередь.

QProcess *process = new QProcess;
process->setReadChannel(QProcess::StandardOutput);

connect(process, &QProcess::readyReadStandardOutput, [](){
    qDebug()<< "readyReadStandardOutput";
});

connect(process, &QProcess::stateChanged, [](QProcess::ProcessState state){
    qDebug()<< "stateChanged"<< state;
});

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [=](){
    qDebug()<< "finsished";
});

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [this, process](int exitCode, QProcess::ExitStatus exitStatus){
    qDebug()<< "finsished";
    if (exitStatus == QProcess::NormalExit && exitCode == 0){
        while (process->canReadLine()) {
           QString line = QString::fromLocal8Bit(process->readLine());
           QRegularExpression regex("\"(.*)\" {(.*)}");
           QRegularExpressionMatch match = regex.match(line);
           names_.push_back(match.captured(1));
           uuids_.push_back(match.captured(2));
        }
    }
    process->deleteLater();
});
process->start("VBoxManage",  {"list", "vms"});
process->waitForFinished(); // This line changes everything
qDebug()<< "leftWaitForFinished";

1 ответ

Решение

Вы не запускаете цикл обработки событий в потоке, где QProcess Экземпляр живет. любой QObject в потоке без цикла обработки событий функционирует только частично - таймеры не будут работать, вызовы в очереди не будут доставляться и т. д. Поэтому вы не можете этого сделать. С помощью QObjectс QtConcurrent::run требует заботы.

По крайней мере, у вас должен быть временный цикл обработки событий в течение всего времени жизни процесса - в этом случае вы должны удерживать QProcess по значению, так как deleteLater не будет выполняться после завершения цикла обработки событий.

QProcess process;
...
QEventLoop loop;
connect(process, &QProcess::finished, &loop, &QEventLoop::quit);
loop.exec();

В противном случае вам нужно сохранить процесс в более надежной нити и сохранить этот дескриптор потока (QThread это всего лишь дескриптор!) в потоке, который имеет цикл обработки событий, который может удалить его, когда это будет сделано.

// This can be run from a lambda that runs in an arbitrary thread

auto thread = new QThread;
auto process = new QProcess;

...

connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
    [this, process](int exitCode, QProcess::ExitStatus exitStatus){
    ...
    process->deleteLater();
    process->thread()->quit();
});

process->start("VBoxManage",  {"list", "vms"});
process->moveToThread(thread);

// Move the thread **handle** to the main thread
thread->moveToThread(qApp->thread());
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();

Увы, это очень глупо, так как вы создаете временные потоки, и это дорого и расточительно. У вас должен быть один дополнительный рабочий поток, в котором вы позаботитесь обо всей работе с небольшим объемом накладных расходов, такой как QProcess взаимодействие. Этот поток всегда должен быть запущен, и вы можете переместить все QProcess и подобные ему экземпляры объектов из параллельных лямбд и т. д.

Другие вопросы по тегам