codekaizer codekaizer - 2 days ago 3
C++ Question

How can I run 3 QProcess in background using the same executable

I have a program that has a

Worker
class and this class has 3
QProcess
es.

Each process will require a
QDate
as an argument.
I want to run all of them in background.

The Problem

If I try to specify more than 3 dates (meaning all processes are running),
the entire program freezes.

I've already tried using
QProcess::startDetached
but it pops up command prompts which I don't like.

I want everything to run in background, 3 processes running simultaneously.

Here's my code:

in
worker.h


#ifndef WORKER_H
#define WORKER_H

#include <QObject>

#include <QDate>
#include <QProcess>
#include <QQueue>

class Worker: public QObject
{
Q_OBJECT

signals:
void processed(QString& date);
void error(QString& error);
void done(QString& done);

public:
Worker();

void run(QDate& start, QDate& end, int delay);

private slots:

void onFinished1(int exitCode, QProcess::ExitStatus exitStatus);
void onError1(QProcess::ProcessError);

void onFinished2(int exitCode, QProcess::ExitStatus exitStatus);
void onError2(QProcess::ProcessError);

void onFinished3(int exitCode, QProcess::ExitStatus exitStatus);
void onError3(QProcess::ProcessError);

private:
QProcess* worker1;
QProcess* worker2;
QProcess* worker3;

QQueue<QDate> date_list;
int running;
};

#endif // WORKER_H


in
worker.cpp


#include "worker.h"

#include <QDebug>
#include <QStringList>

Worker::Worker()
{
worker1 = new QProcess(this);
worker2 = new QProcess(this);
worker3 = new QProcess(this);

connect(worker1, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onFinished1(int,QProcess::ExitStatus)));
connect(worker1, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(onError1(QProcess::ProcessError)));

connect(worker2, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onFinished2(int,QProcess::ExitStatus)));
connect(worker2, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(onError2(QProcess::ProcessError)));

connect(worker3, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onFinished3(int,QProcess::ExitStatus)));
connect(worker3, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(onError3(QProcess::ProcessError)));

running = 0;
}

void
Worker::onFinished1(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "worker 1: "
<< "Exit Code: " << exitCode
<< "Exit Status: " << exitStatus;

running -= 1;
if (date_list.empty() && running == 0) {
//system("rm output.xlsx");
system("copy .\\extractor\\output.xlsx output.xlsx");
emit done(QString("Done generating output.xlsx"));
}
}

void
Worker::onFinished2(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "worker 2: "
<< "Exit Code: " << exitCode
<< "Exit Status: " << exitStatus;

running -= 1;
if (date_list.empty() && running == 0) {
//system("rm output.xlsx");
system("copy .\\extractor\\output.xlsx output.xlsx");
emit done(QString("Done generating output.xlsx"));
}
}

void
Worker::onFinished3(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "worker 3: "
<< "Exit Code: " << exitCode
<< "Exit Status: " << exitStatus;

running -= 1;
if (date_list.empty() && running == 0) {
//system("rm output.xlsx");
system("copy .\\extractor\\output.xlsx output.xlsx");
emit done(QString("Done generating output.xlsx"));
}
}

void
Worker::onError1(QProcess::ProcessError /* error */)
{
qDebug() << "worker 1: "
<< "Error: " << worker1->errorString();

running -= 1;
emit error(worker1->errorString());
}

void
Worker::onError2(QProcess::ProcessError /* error */)
{
qDebug() << "worker 2: "
<< "Error: " << worker2->errorString();

running -= 1;
emit error(worker2->errorString());
}

void
Worker::onError3(QProcess::ProcessError /* error */)
{
qDebug() << "worker 3: "
<< "Error: " << worker3->errorString();

running -= 1;
emit error(worker3->errorString());
}

void
Worker::run(QDate& start, QDate& end, int delay)
{
qDebug() << __FUNCTION__
<< start
<< end
<< delay;

while(start <= end) {
date_list.push_back(start);
start = start.addDays(1);
}

QProcess * current;
QDate processed_date;
QStringList args;

qDebug() << date_list;

while(! date_list.empty()) {
bool has_available = false;
if (worker1->state() != QProcess::Running) {
current = worker1;
processed_date = date_list.dequeue();
has_available = true;
} else if (worker2->state() != QProcess::Running) {
current = worker2;
processed_date = date_list.dequeue();
has_available = true;
} else if (worker3->state() != QProcess::Running) {
current = worker3;
processed_date = date_list.dequeue();
has_available = true;
}

// Has available worker, start worker
if (has_available) {
running += 1;

QString sdate = processed_date.toString("yyyy-MM-dd");
args << sdate << QString::number(delay);
current->start(".\\release\\extractor\\content.exe", args);
args.clear();
emit processed(sdate);
}
qDebug() << "running: " << running;
}
}


Any ideas?

Answer

Your program freezes because it is busy all the time with waiting on QProcess to be finished, i.e. while(1). So, you do not return to the main event loop before all your dates are processed. If you wait long enough, your program will unfreeze.

Different solutions exist:

  • Use the finished event to start processing a new date (until the list is empty), i.e. remove the infinite/waiting loop while(1).

  • Perform Worker::run in a separate thread.

  • Call QCoreApplication::processEvents to process the pending events, for example when has_available is false.

Note that I prefer the first solution as Qt is an event driven system.