2016-08-19 1 views
1

Im folgenden Code wird der QProcess durch das Weglassen des waitForFinished() nicht mehr mit der Ausgabe seines Signals verbunden. Was zum Teufel ist damit falsch? Ist das ein Qt Bug? (5.7). Beachten Sie, dass dieser Code parallel zur Ausführung von QtConcurrent ausgeführt wird. Aber das sollte nichts ändern, oder? Afaik sendet Signale in anderen Threads in Ordnung, obwohl sie in die Warteschlange gestellt werden.QProcess gibt seine Signale nicht aus, wenn er nicht wartetForFinished()

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"; 
+0

Haben Sie all diesen Code innerhalb einer Lambda-Funktion, die an 'QtConcurrent :: run 'übergeben wird? – Mike

Antwort

3

Sie sind nicht eine Ereignisschleife in dem Thread ausgeführt wird, wo die QProcess Instanz lebt. Alle QObject in einem Thread ohne eine Ereignisschleife ist nur teilweise funktionsfähig - Timer werden nicht ausgeführt, Anrufe in der Warteschlange werden nicht zugestellt, usw. So können Sie das nicht tun. Die Verwendung von QObject s mit QtConcurrent::run erfordert Sorgfalt.

Zumindest sollten Sie eine temporäre Ereignisschleife haben, solange der Prozess lebt - in diesem Fall sollten Sie QProcess als Wert halten, da deleteLater nicht ausgeführt wird, nachdem die Ereignisschleife beendet wurde.

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

Andernfalls müssen Sie den Prozess in einem haltbareren Thread zu halten und zu halten, dass Thread-Handle (QThread ist aber ein Griff!) In einem Thread, der eine Ereignisschleife hat, die darüber verfügen kann, wenn es fertig ist .

// 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(); 

Leider ist dies sehr albern, da Sie temporäre Threads erstellen und das ist teuer und verschwenderisch. Sie sollten einen zusätzlichen Worker-Thread haben, wo Sie alle Arbeiten mit geringem Overhead wie QProcess Interaktion kümmern. Dieser Thread sollte immer ausgeführt werden, und Sie können alle QProcess und ähnliche Objektinstanzen aus simultanen Lambdas usw. dorthin verschieben.

+0

Es gibt mehrere Jobs wie diese, die getan werden müssen. Ich benutze Concurrent, weil ich auf diese Weise alle Kerne problemlos verwenden kann. Warum genau funktioniert es nicht? Ist es das Lambda? Das Objekt, auf das ich rufe, läuft im Haupt-Thread. Wird das Lambda als Mitglied funktionieren? – ManuelSchneid3r

+0

Entschuldigung, das hört sich blöd an, aber sind nicht Threads von 'QThreadPool' bereits eine Event-Schleife? (um beispielsweise Funktionen auszuführen, die an 'QThreadPool :: start' übergeben werden) – Mike

+1

Ich habe genau gesagt, warum es nicht funktioniert: Es ist, weil für ein' QObject' wie ein 'QProcess' der Thread funktional ist es läuft auf muss eine Event-Schleife laufen! Es hat nichts mit Lambdas oder so etwas zu tun. Das Objekt, bei dem Sie 'run' aufrufen, ist irrelevant: Das Lambda wird in einem Worker-Thread ausgeführt, in dem keine Ereignisschleife vorhanden ist. –

Verwandte Themen