I am trying to perform multiple long-running initializers on threads. I would like to get results as soon as they are available, regardless of order.
I have tried with a simple thread but got stuck where I needed to create multiple threads... and manage them... And the idea of thread pool, futures, thread watcher kept coming up in my research. So, this is my latest try:
struct Initializers {
BigObject *newBigObject;
bool newValue;
int initValue;
};
class BigObject {
public:
BigObject(QString) {}
void initialize(bool, int) {}
};
class MyClass : public QObject {
Q_OBJECT
public:
MyClass() {
connect(&m_futureWatcher, SIGNAL(resultReadyAt(int)), this, SLOT(initUsingFutureSlot(int)));
connect(&m_futureWatcher, SIGNAL(finished()), this, SLOT(initUsingFutureSlot()));
}
void doImportantWork(QStringList someNames);
private slots:
void initUsingFutureSlot(int);
void initUsingFutureSlot();
private:
void createThreadConnections();
//void initComplete(BigObject *);
QFutureWatcher<void> m_futureWatcher;
QVector<Initializers> m_initializersVector;
};
void longInitializer(Initializers &initializers) {
qDebug() << "Running in thread" << QThread::currentThreadId();
initializers.newBigObject->initialize(initializers.newValue, initializers.initValue);
}
void MyClass::doImportantWork(QStringList someNames) {
qDebug() << QString("Long running process using %1 thread(s)").arg(QThread::idealThreadCount());
// Prepare the vector.
foreach (QString someName, someNames) {
BigObject *newBigObject = new BigObject(someName);
Initializers initializers;
initializers.newBigObject = newBigObject;
initializers.newValue = m_appValue;
initializers.initValue = m_appInit;
m_initializersVector.append(initializers);
}
// Start the work
m_futureWatcher.setFuture(QtConcurrent::map(m_initializersVector, longInitializer));
//m_futureWatcher.waitForFinished();
}
// This does not get called
void MyClass::initUsingFutureSlot(int index) {
qDebug() << "Finished" << index;
//initComplete(m_initializersVector.at(index).newBigObject);
}
// This does not get called
void MyClass::initUsingFutureSlot() {
qDebug() << "Finished"
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyClass task;
task.doImportantWork(QStringList() << "Hello" << "World");
return app.exec();
}
This version of my code is based on QT5 Tutorial QProgressDialog with QtConcurrent which is the only complete example I was able to find that works with multiple threads.
One problem - their QFutureWatcher
's type is void
- and I could not find any way to modify the code to use a different type - and not give compile errors. That is why I made the vector they use a class member, to grab the values from there.
My understanding is that the vector gets the slot applied to every member of it - based on documentation.
So, I ran it, but nothing happens. The debug message shows that the threads are running. But the resultReadyAt
or finished
and their corresponding slots are not called. In addition, closing the program says QThread: Destroyed while thread is still running
, so they don't finish and close, as I expected them being handled by the Watcher.
I can make things work with QtConcurrent::run
!!!!
I am stuck.
Questions:
- How can I make this QFuture / QFutureWatcher work ? What am I missing in my code or what am I doing wrong to make the threads not finish ?
- How can I make the template non-void (to retrieve results easier ?)
- I have
m_futureWatcher.waitForFinished();
commented because that would block the GUI thread, I think - is that correct ? - Is there a better option to run multiple threads ? the word
ThreadPool
comes to mind but I have seen no examples that I can use - that can notify of completion. I did find a nice example to emulate a ThreadPool (NetResultsIT), but I expect Qt should have something functional. - When closing program, I get
QThread: Destroyed while thread is still running
. I was under the assumption that threads are closed when done, how should I clean up ? Is deleting the watcher enough ?
I am so lost, fighting these threads for over a week, and they're winning :( Thank you for any help or example.
Using Qt 5.15
Here is a simple example how
QThreadPool
worksoutput: