Correct way to signal QObject on another thread? - c++

Let's say I have a slave object that lives in another thread. I want to tell it to do A, B, C on that thread. I can think of 3 ways of doing it:
(1) Using QTimer::singleShot
(2) Using QMetaObject::invokeMethod
(3) Creating another master object and connecting its signal to the slave
Following is an example:
class slave : public QObject
{
QThread thread_;
friend class master;
void do_A(params);
void do_B(params);
void do_C(params);
public:
slave() { thread_.start(); moveToThread(&thread_); }
~slave() { thread_.quit(); thread_.wait(); }
void que_A(params) { QTimer::singleShot(0, [&](){ do_A(params); }); } // (1)
void que_B(params) { QMetaObject::invokeMethod(this, "do_B", params); } // (2)
}
class master : public QObject // (3)
{
Q_OBJECT
public:
master(slave* s) { connect(this, &master::que_C, s, &slave::do_C); }
void do_C(params) { emit que_C(params); }
signals:
void que_C(params);
}
My concerns are:
(1) I am abusing QTimer.
(2) Using strings for signals/slot is so qt4. Qt5 uses new syntax.
(3) Too much boilerplate.
Is any of the methods considered more correct compared to the others? Or can anybody think of a better way?
Please include your reasoning (not just opinion) why one method should be chosen over others.
UPDATE:
In my real-world application I have another class -- let's call it owner -- that owns several slaves. The owner needs to tell different slaves to do different things (A, B or C) depending on user input. The slaves are stateful objects, so I cannot see an easy way of using concurrency functions (eg, std::async or QtConcurrency).

Well, I will not comment (1) and (2) but I must say that (3) is the one usually used. However, I understand your concern about too much boilerplate. After all, creating a separate signal, say doActionA(), connecting it via QueuedConnection to some real actionA() and at last emitting it... too much noise and useless moves.
Indeed, the only benefit it gives you is a loose coupling (you can send a signal being not aware of existence of slots connected to it). But if I create a signal with a name doActionA() of course I am aware of actionA() existence. So then the question is starting to raise "Why do I have to write all this stuff?"
Meanwhile, Qt kind of provides the solution to this problem giving you the ability to post your own events to any event loop (and as you know QThread has one). So, implement it once and you do not need to write a lot of connect emit stuff any more. Also, I suppose I is more efficient because all in all, every slot invokation via QueuedConnection just posts an event in an event loop.
Here InvokeAsync posts the event for member function execution into the event loop of the thread where QObject lives:
#include <QCoreApplication>
#include <QEvent>
#include <QThread>
#include <string>
#include <iostream>
#include <type_traits>
#include <functional>
template<typename T, typename R, typename ... Params, typename... Args>
void InvokeAsync(T* object, R (T::*function)(Params...), Args&&... args)
{
struct Event : public QEvent
{
std::function<R ()> function;
Event(T* object, R (T::*function)(Params...), Args&& ... args)
: QEvent{ QEvent::None },
function{ std::bind(function, object, std::forward<Args>(args)...) }
{
}
~Event() { function(); }
};
QCoreApplication::postEvent(object, new Event{ object, function, std::forward<Args>(args)... });
}
struct Worker : QObject
{
void print(const std::string& message, int milliseconds)
{
QThread::currentThread()->msleep(milliseconds);
std::cout << message
<< " from thread "
<< QThread::currentThreadId() << std::endl;
}
};
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
std::cout << "GUI thread " << QThread::currentThreadId() << std::endl;
QThread thread;
thread.start();
Worker worker;
worker.moveToThread(&thread);
InvokeAsync(&worker, &Worker::print, "Job 1", 800);
InvokeAsync(&worker, &Worker::print, "Job 2", 400);
InvokeAsync(&worker, &Worker::print, "Job 3", 200);
a.exec();
return 0;
}
Output:
GUI thread 00000000000019C8
Job 1 from thread 00000000000032B8
Job 2 from thread 00000000000032B8
Job 3 from thread 00000000000032B8
As you see all jobs where done in a different thread in order of their invocation. Qt guarantees that events with the same priority are processed in order as they were posted.
Also it is OK, if worker lives in GUI thread. Events will be just posted in GUI event loop and processed later (that is why I called those Async).
If you see any mistakes or have some remarks, please, write in comments and we will figure it out.

I am not pretty sure I understood well the question.
So I will explain here how to manage thread in Qt (in summarized way).
First, QThreadclass is not really meant to be inherited from.
Instead, make something like this :
class Slave : public QObject {
Slave(QThread *thread) {moveToThread(thread);)
};
QThread thread;
Slave slave(&thread);
After, you normally can use signal and slots in the normal way.
However, if your objective is only to "run a function inside another thread", maybe QConcurrent could be a better way?

In fact, the Qt documentation provides a very nice example that probably answers your question on how to use QThread. It's the following:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString &parameter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
The idea here is simple. You have a worker, which is running in the thread. You also have the controller, which is basically your main thread that submits work to the other thread. The thread will (thread-safely) emit the resultReady() signal when it's finished. Signals and slots are thread-safe here, as long as they use Qt::QueuedConnection to communicate.

Related

Creating QNetworkAccessManager in another thread

I have a QNetworkAccessManager created in another thread.
The network is meant to be used only in MyMegaThread.
QNetworkAccessManager is created from the thread's run method:
mp_manager.reset(new QNetworkAccessManager{this});
On creation I get such a message in console:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is MyMegaThread(0x237eabd0ee0), parent's thread is QThread(0x237e70742a0), current thread is MyMegaThread(0x237eabd0ee0)
This message is totally harmless, but I wonder which parent the manager is supposed to have.
I suspect it happens because the MyMegaThread instance is created in the main thread, but I need a parent created in MyMegaThread instead.
What is an idiomatic way of doing this?
Parent is MyMegaThread(0x237eabd0ee0), parent's thread is
QThread(0x237e70742a0), current thread is MyMegaThread(0x237eabd0ee0)
The issue does not relate to QNetworkAccessManager.
Here is the demo to reproduce the warning.
#include <QDebug>
#include <QThread>
class MyMegaThread : public QThread
{
Q_OBJECT
public:
using QThread::QThread;
protected:
void run() override {
qDebug()<<QThread::currentThread()<<this->thread();
new QObject(this);
}
};
// main
MyMegaThread m;
m.start();
Output:
MyMegaThread(0x60fe18) QThread(0x16a7c48)
It's rule of QObject:
All QObjects must live in the same thread as their parent.
Consequently:
setParent() will fail if the two QObjects involved live in different
threads. When a QObject is moved to another thread, all its children
will be automatically moved too. moveToThread() will fail if the
QObject has a parent. If QObjects are created within QThread::run(),
they cannot become children of the QThread object because the QThread
does not live in the thread that calls QThread::run().
http://doc.qt.io/qt-5/qobject.html#thread-affinity
Have to make sure this code new QObject running QThread be same as given parent QObject thread.
mp_manager.reset(new QNetworkAccessManager{this});
No, the message is not harmless at all. The object you have created has a null parent and no reference on the thread association and thus its thread() method may return a dangling pointer at any time. It cannot safely use timers nor receive cross-thread calls. It is basically as useless object, and you're asking for undefined behavior to strike. This shouldn't be a warning, but a failure. Qt did you a disservice here by allowing you to continue.
The idiomatic way of doing it is first of all not to derive from QThread. QThread is a thread handle. It wraps a system resource. Put all of your functionality into a regular QObject moved into a QThread. The idiomatic way to endlessly "do stuff" on any thread, including the main thread, is to use a zero-duration timer. Note that zero-duration timers have nothing to do with timing at all. They are essentially event loop handles, calling them a timer is a misnomer.
To wit:
// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-simple-50632807
#include <QtNetwork>
class Thread final : public QThread {
Q_OBJECT
public:
void takeObject(QObject *obj) {
obj->moveToThread(this);
}
~Thread() override {
requestInterruption();
quit();
wait();
}
};
class Class : public QObject {
Q_OBJECT
QBasicTimer m_workTimer;
QNetworkAccessManager m_manager{this};
void doWorkChunk() {
qDebug() << "tick...";
QThread::sleep(1); // emulate a blocking operation
}
protected:
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != m_workTimer.timerId())
return;
doWorkChunk();
}
public:
explicit Class(QObject *parent = {}) : QObject(parent) {
m_workTimer.start(0, this);
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
Class object;
Thread workThread;
workThread.start();
workThread.takeObject(&object);
QTimer::singleShot(3000, &QCoreApplication::quit);
return app.exec();
}
#include "main.moc"
The QBasicTimer::stop: Failed. Possibly trying to stop from a different thread warning is relatively benign and indicates an internal timer handle leak. For a workaround, see this answer.

How to block thread until a function returns in another thread?

If I have a main thread and a separate, permanent worker QThread:
// Main thread where the event loop and QCoreApplication runs.
class NetworkController : public QObject {
public:
Q_OBJECT
void connect_to_ap()
{
WifiAP *myAP = netMgr.get_best_ap();
myAP->connect("my_psk_password");
}
// This is a class from a 3rd-party library, which contains, owns
// and manages the lifetime of "WifiAP" instances.
NetworkManager netMgr;
};
// Separate thread where a state machine runs.
class StateMachineWorker : public QObject {
public:
Q_OBJECT
void on_ready_to_connect_event()
{
// HERE: How to trigger NetworkController::connect_to_ap() and
// *block* until it returns.
}
NetworkController *pNetCtrlr;
}
When the state machine class enters a certain state it should connect to an AP (Access Point). The NetworkController has the functionality to connect to an AP.
I am trying to figure out a way that the state machine can do this in a thread-safe way. The problem is NetworkManager is always updating its list of WifiAP instances: they are created and destroyed often.
It would not be thread-safe for StateMachineWorker to call pNetCtrlr->connect_to_ap() directly (as the NetworkManager in the NetworkController thread could at the same time delete the WifiAP instance).
So what I would like is in StateMachineWorker::on_ready_to_connect_event() to somehow signal the NetworkController to run its connect_to_ap() method in NetworkController's own thread, and to block the StateMachineWorker thread until connect_to_ap() has finished doing its stuff. The reason I want the state machine to be blocked is if I did not block and let it enter the event loop, it could receive some event that would make it transition to another state before connect_to_ap() has finished executing; this must not happen.
Mutex locks to protect the list of WifiAP in NetworkManager would not work as they would need to be inserted inside the 3rd-party library.
You can use QMetaObject::invokeMethod with parameter Qt::BlockingQueuedConnection. This connection type adds all the blocking logic for you and you don't have to change the third party library at all.
A general example:
objects.h
#include <QObject>
#include <QThread>
#include <iostream>
class Object : public QObject {
Q_OBJECT
public slots:
void foo() {
std::cout << "Hello";
thread()->msleep(2000);
std::cout << " world!" << std::endl;
}
};
class Caller : public QObject {
Q_OBJECT
public:
void call(Object* o) {
std::cout << "Calling..." << std::endl;
metaObject()->invokeMethod(o, "foo", Qt::BlockingQueuedConnection);
std::cout << "Finished!" << std::endl;
}
};
main.cpp
#include <QCoreApplication>
#include "objects.h"
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
QThread t;
Object o;
o.moveToThread(&t);
t.start();
Caller().call(&o);
return a.exec();
}
The counterpart is that the method to be called must be a slot. If connect_to_ap is not already a slot you can create a bridge object that do the job, as explained below.
Have in mind that this bridge object must live in the same thread as the NetworkController (in your case the main thread), so the slot is queued in the correct events loop. You can take a look at QObject::moveToThread for further information.
A quick draft would be something like:
class NetworkControllerBridge : public QObject {
Q_OBJECT
NetworkController* nc;
public:
NetworkControllerBridge(NetworkController* nc_) : nc(nc_) {}
public slots:
void connect_to_ap() {
nc->connect_to_ap();
}
};
// ...
void on_ready_to_connect_event()
{
NetworkControllerBridge bridge(pNetCtrlr);
bridge.moveToThread(qApp->thread());
metaObject()->invokeMethod(&bridge, "connect_to_ap", Qt::BlockingQueuedConnection);
}
Update
Another way to call a method through invokeMethod is to mark it as Q_INVOKABLE. Although you still need the bridge since you cannot modify the library, I mention this for completeness of the answer.

How can I have QThread emit a heap-allocated QObject without leaking?

My situation is that I have a QWidget-derived class, MyWidget, that will create a QThread-derived class (WorkerThread) to do some uninterruptible, blocking work in its run() method. The results of this are a heap-allocated instance of a QObject-derived class (DataClass) which is then received and processed by MyWidget. MyWidget is a transitory widget, though, and may be deleted while WorkerThread is still running due to user action.
Here's some pseudo-code to illustrate this:
#include <QThread>
#include <QWidget>
class DataClass : public QObject {
Q_OBJECT
public:
// contains some complex data
};
class WorkerThread : public QThread {
Q_OBJECT
public:
virtual void run() {
DataClass *result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
emit workComplete(result);
}
signals:
void workComplete(DataClass *);
};
class MyWidget : public QWidget {
Q_OBJECT
public:
void doBlockingWork() {
WorkerThread *worker = new WorkerThread;
connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
worker->start();
}
public slots:
void processData(DataClass *result) {
// Do some stuff
delete result;
// Assuming MyWidget still exists when WorkerThread has finished, no memory has leaked
}
};
Normally the correct "Qt" way to return the results of a worker thread is to have it emit a signal with its arguments being the result of its work, as illustrated above. That's fine for data that can be copied, but since the result is a pointer to a heap-allocated object, I have to be careful to make sure that memory gets freed.
And normally that wouldn't be a problem, because since WorkerThread has finished, I can safely pass the pointer to DataClass to MyWidget, have it process DataClass, and then free it.
The problem is that, as I said earlier, MyWidget is transitory and may be destroyed before WorkerThread is finishing. In this scenario, how can I ensure that the instance of DataClass gets freed one way or the other?
In particular, I'm looking for solutions that have some elegance to them, meaning that it takes advantage of Qt's features and preferably makes it so that WorkerThread maintains its separation from MyWidget so that WorkerThread doesn't need to know anything about it or any other class that might create it. I'm also open to ideas that improve upon the pattern that I'm already using.
Use smart pointer (e.g., QSharedPointer) instead a normal pointer:
DataClass *result = new DataClass;
should be replaced with
QSharedPointer<DataClass> result = QSharedPointer<DataClass>(new DataClass);
Then, you could safely pass it somewhere and do not worry about deleting it. When it is out of the last scope where it can be used, the object will be automatically destroyed.
The worker should push the result to the main thread, to indicate that it's safe to use there (per QObject semantics). The result should be auto-deleted in the main thread after everyone interested has been notified of the completion of the work. It is a minimal change:
void run() override {
auto result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread()); // added
emit workComplete(result);
QObject::connect(this, &QThread::finished, result, &QObject::deleteLater); // added
}
You're guaranteed that deleteLater will be invoked after the last handler of workComplete has finished in the main thread.
A single object in the main thread might wish to retain the results longer. This can be indicated by setting the parent on the result object. The object shouldn't be deleted then:
...
QObject::connect(this, &QThread::finished, result, [result]{
if (!result->parent()) result->deleteLater();
});
If you intend that multiple objects in the main thread retain the results longer, you should be using a QSharedPointer in the workComplete's argument, and you must never set the parent of the results: a non-null parent and a QSharedPointer are mutually incompatible: the former indicates a unique ownership by a parent, the latter indicates a shared ownership.
It is necessary to move the DataClass object to the main thread to avoid a race on DataClass::thead() and to allow deleteLater to work:
Worker Thread: emit workComplete(result)
Main Thread: start using result, result.thread() is the worker instance.
Worker Thread: finishes
Main Thread: result.thread() is now nullptr while the main thread is using it.
This might not be a problem, but usually indicates poor design. As soon as you start using more QObject features of DataClass, it turns the latent bug into a real bug: e.g. deleteLater won't work, timers won't work, etc.
Furthermore, destructing a QObject in any thread other than its thread is not supported. Suppose that you had your original code. The following could happen and leads to undefined behavior:
Worker Thread: emit workComplete(result)
Main Thread: start using result, result.thread() is the worker instance.
Main Thread: delete result. QObject::~QObject is invoked in qApp->thread() but result->thread() is the different, still live instance of the worker thread.
If you wish to catch such issues, add:
DataClass::~DataClass() {
Q_ASSERT(thread() == nullptr || thread() == QThread::currentThread());
...
}
It's OK to destruct a threadless object, but such objects are not fully functional: you can't deleteLater them, their timers don't work, they don't receive events, etc.
The necessity of a parent check prior to deleteLater depends on whether you intend to prolong the existence of the result past the code connected to workComplete.
The "obvious" use of a shared pointer doesn't make it clear which thread can safely access the result iff the result isn't thread-safe. It also does nothing by itself to fix the fact that once the worker finishes, the QObject is half-functional as there's no event loop associated with it. I believe that your intent is that only one thread may own the result, so that its methods don't have to be thread-safe. Luckily, QObject's semantics already express this clearly: the object's thread() is the one authorized to act on the object.
Any recipients of workComplete in the main thread will get to process the results before they vanish. If any object in the main thread wants to take ownership of the result, it can - by setting the parent. Otherwise, as soon the workComplete handlers are done, if none have claimed ownership, the result will get deleted from the main event loop.
Change the QTimer::singleShot(1000, w.data(), [&]{ w.reset(); }) timer to 2500ms to have the widget outlive the worker thread and note the difference in behavior depending on whether it claimed ownership.
Complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/worker-shared-37956073
#include <QtCore>
struct DataClass : public QObject {
DataClass() { qDebug() << __FUNCTION__; }
~DataClass() { qDebug() << __FUNCTION__; }
};
void doSomeReallyLongUninterruptibleWork(DataClass*) { QThread::sleep(2); }
class WorkerThread : public QThread {
Q_OBJECT
public:
void run() override {
auto result = new DataClass;
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread());
emit workComplete(result);
QObject::connect(this, &QThread::finished, result, [result]{
if (!result->parent()) {
qDebug() << "DataClass is unclaimed and will deleteLater";
result->deleteLater();
}
});
}
Q_SIGNAL void workComplete(DataClass*);
};
class MyWidget : public QObject {
void processData(DataClass * result) {
// Do stuff with result
// Retain ownership (optional)
if (true) result->setParent(this);
}
public:
void doBlockingWork() {
auto worker = new WorkerThread;
connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
worker->start();
}
~MyWidget() { qDebug() << __FUNCTION__; }
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QScopedPointer<MyWidget> w{new MyWidget};
w->doBlockingWork();
QTimer::singleShot(1000, w.data(), [&]{ w.reset(); });
QTimer::singleShot(3000, qApp, &QCoreApplication::quit);
return app.exec();
}
#include "main.moc"
You could also forgo the use of an explicit thread, and use QtConcurrent::run instead. There's no clear advantage to that, I'm showing it here just to indicate that either approach is feasible.
#include <QtConcurrent>
struct DataClass : public QObject {
Q_SIGNAL void ready();
Q_OBJECT
};
// Let's not pollute the default pool with long-running stuff
Q_GLOBAL_STATIC(QThreadPool, longPool)
class MyWidget : public QObject {
void processData(DataClass * result) {
// Do stuff with result
// Retain ownership (optional)
if (true) result->setParent(this);
}
public:
void doBlockingWork() {
auto result = new DataClass;
connect(result, &DataClass::ready, this, [=]{ MyWidget::processData(result); });
result->moveToThread(nullptr);
QtConcurrent::run(longPool, [result]{
result->moveToThread(QThread::currentThread());
doSomeReallyLongUninterruptibleWork(result);
result->moveToThread(qApp->thread());
emit result->ready();
QTimer::singleShot(0, result, [result]{
if (!result->parent()) result->deleteLater();
});
});
}
};

Qt 5 : update QProgressBar during QThread work via signal

I'm trying to update a QProgressDialog (owned by a QMainWindow class) along the execution of a QThread who process some time consuming operations. The thread emit some signals during operation in order to inform the calling app about progression. I'm looking to connect the progress signal emitted by the thread to the setValue slot of the QProgressDialog in order to update the progress bar.
It doesn't work ! The progress dialog is not displayed. If I add a slot in my QMainWindow and connect it to the worker progress signal in order to display the value given by the thread throught qDebug output, I see that signals seems to be stacked during the threaded operation and unstacked only at the end of the thread.
I have tryed the DirectConnection connect's option without any success.
Here is my code :
qapp.cpp
#include "qapp.h"
#include <threaded.h>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>
#include <QProgressDialog>
QApp::QApp(QWidget *parent) :
QMainWindow(parent)
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QWidget *window = new QWidget(this);
window->setLayout(mainLayout);
setCentralWidget(window);
QPushButton *button = new QPushButton("Run");
mainLayout->addWidget(button);
connect(button, SIGNAL(clicked(bool)), this, SLOT(doSomeWork()));
}
void QApp::doSomeWork()
{
qDebug() << "do some work";
Threaded worker;
worker.doHeavyCaclulations();
QProgressDialog progressDialog("Copying files...", "Abort Copy", 0, 10000, this);
progressDialog.setWindowModality(Qt::WindowModal);
progressDialog.setMinimumDuration(0);
progressDialog.setValue(0);
connect(&worker, SIGNAL(progress(int)), &progressDialog, SLOT(setValue(int)));
connect(&worker, SIGNAL(progress(int)), this, SLOT(displayProgress(int)));
worker.wait();
qDebug() << "end of thread";
}
void QApp::displayProgress(int value)
{
qDebug() << "data received" << value;
}
QApp::~QApp()
{
}
threaded.cpp :
#include "threaded.h"
#include <QDebug>
Threaded::Threaded(QObject *parent) : QThread(parent)
{
}
void Threaded::doHeavyCaclulations()
{
if (!isRunning())
{
qDebug() << "start thread" ;
start();
}
}
void Threaded::run()
{
qDebug() << "running big loop";
for(double k = 0 ; k < 10000 ; k++)
{
qDebug() << k;
emit progress(k);
}
}
qapp.h
#ifndef QAPP_H
#define QAPP_H
#include <QMainWindow>
class QApp : public QMainWindow
{
Q_OBJECT
public:
explicit QApp(QWidget *parent = 0);
~QApp();
private:
private slots:
void doSomeWork();
void displayProgress(int value);
};
#endif // QAPP_H
threaded.h
#ifndef THREADED_H
#define THREADED_H
#include <QObject>
#include <QThread>
class Threaded : public QThread
{
Q_OBJECT
public:
explicit Threaded(QObject *parent = 0);
void doHeavyCaclulations();
void run();
private:
signals:
void progress(int value);
public slots:
};
#endif // THREADED_H
The output of this code with k < 100 is :
do some work
start thread
running big loop
0
1
2
3
[...]
97
98
99
end of big loop
end of thread
data received 17
data received 18
data received 19
[...]
data received 99
If I remplace
worker.wait();
by
int k=0;
while(worker.isRunning())
{
qDebug() << "main " << k;
k++;
}
I get outputs of the thread and output of the calling method interleaved. It confirm that my thread is independant of the calling method.
Any idea about what I'm doing wrong ?
Absolutely wrong using of QThread). See what is the correct way to implement a QThread... (example please...). You need to learn thread's basics.
Your mistakes:
1. Create a static thread object in a local scope;
2. Wait for its finish in the main thread;
3. Don't start the thread;
4. Direct call method doHeavyCaclulations() in the main thread;
5. emit signal without working event loop for its deliver...
For your purpose you need:
Don't inherit QThread. Just create simple Work class with the necessary function:
class Work: public QObject
{
Q_OBJECT
public:
Work(){};
virtual ~Work(){};
public slots:
void doHeavyCaclulations() { /* do what you need and emit progress signal */ };
signals:
void progress(int);
}
// Then:
void QApp::doSomeWork()
{
//...
QThread* thread = new QThread(parent);
Work* worker = new Work; // Do not set a parent. The object cannot be moved if it has a parent.
worker->moveToThread(thread);
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), worker, SLOT(doHeavyCaclulations()));
connect(worker, SIGNAL(progress(int)), &progressDialog, SLOT(setValue(int)));
thread->start();
//...
}
QThread has one very important thing you have to always remember when working with it - only the run() actually runs in a separate thread.
Whenever you create an instance of QThread this instance's thread affinity (the thread it belongs to) is the same thread where you have created it in. What's the big deal with that and what does it have to do with my slots and signals you may ask? Well, it has a lot to do with these things. Because only run() runs inside a separate thread you have to consider the following:
Signals belong to the instance ergo signals have a different thread affinity then the run()
Slots belong to the instance ergo slots have a different thread affinity then the run() - accessing shared data that is processed both inside a slot and inside run() requires explicitly employing thread-safety mechanisms such as mutexes and semaphores
If you do a lot of stuff inside your slots you will still freeze your UI as if you are not using your QThread
That said there are some scenarios where you may want to/have to employ slots and signals in a QThread but such implementation would have to be directed towards controlling the instance of QThread and not what it's actually running in a separate thread (using run()).
Here is a small demo I have written as a demonstration of how to implement slots and signals and interact with a separate thread using QObject. It employs slots and signals. Note that the usage of QThread is actually not necessary. You can also use a QRunnable for example (though you have to explicitly tell it to inherit from QObject too or to use a separate subclass of QObject created by you because QRunnable doesn't support slots and signals (it's not a subclass of QObject).
The advantage of using a QObject is that you can move its instance to the thread that is change it's thread affinity so that it completely runs in that separate thread (slots included). You can also put multiple QObject instances inside a single QThread if you want to. When inheriting a QThread and using it instead of this model you are limiting your options quite a bit.
So my advice here is dump the QThread implementation and go for the QThread + QObject (also know as Worker design pattern) way of doing things (for this particular scenario that is).

Sending parameters from GUI thread to work thread

I have got some problem in Qt.
I assign some parameters in GUI thread:
newton.h (work thread) :
class Newton : public QThread
resic.cpp (GUI thread) :
.
.
.
Newton mythread;
resic::resic(QWidget *parent) :
QWidget(parent),
ui(new Ui::resic)
{
ui->setupUi(this);
mythread.start();
}
void resic::on_PushButton_clicked()
{
w1=ui->doubleSpinBox_2->value();
um1=ui->doubleSpinBox->value();
alpha1=ui->doubleSpinBox_3->value();
et01=ui->doubleSpinBox_4->value();
Er1=ui->doubleSpinBox_11->value();
Rx1=ui->doubleSpinBox_12->value();
xa1=ui->doubleSpinBox_8->value();
xb1=ui->doubleSpinBox_9->value();
q1=ui->doubleSpinBox_10->value();
ya1=(q1-2*q1);
yb1=ui->doubleSpinBox_10->value();
maxl1=ui->spinBox->value();
}
And I want to send these parameters after click on the button to the work thread and then run some calculation in this work thread.
But I don't know how send parameters.
Can you give me some advice, please?
Thank you very much.
A simple solution to running a piece of code in a worker thread is to leverage Qt Concurrent framework. Another solution is to put the worker into a QObject, and use a QThread directly.
struct Parameters {
double w1, um1, alpha1, et01, ...;
};
struct Result {
...
};
Result calculate(const Parameters & p) {
...
}
class resic : public QWidget {
...
QFutureWatcher<Result> m_futureWatcher;
QScopedPointer<Ui::resic> ui;
Q_SLOT void onResults();
...
};
resic::resic(QWidget * parent) : QWidget(parent), ui(new Ui::resic)
{
connect(&m_futureWatcher, SIGNAL(finished()), SLOT(onResults()));
}
resic::~resic() {}
Parameters resic::get()
{
Parameters p;
p.w1=ui->doubleSpinBox_2->value();
p.um1=ui->doubleSpinBox->value();
p.alpha1=ui->doubleSpinBox_3->value();
p.et01=ui->doubleSpinBox_4->value();
...
return p;
}
void resic::on_PushButton_clicked()
{
Parameters const p(get());
QFuture<Result> future = QtConcurrent::run(&calculate);
m_futureWatcher.setFuture(future);
}
void resic::onResults()
{
Result const r = m_futureWatcher.result();
...
}
It is important to remember that a QThread object usually lives in the thread where it was created, not in the thread that it manages. This oft-overlooked detail means that a QThread's slots will be executed in the context of its home thread, not in the context of the thread it is managing. For this reason, implementing new slots in a QThread subclass is error-prone and discouraged.
You can inherit the class "Newton" from QObject create your object of this class on the heap and move it to a new thread.
It can be done like:
mythread = new Newton();
QThread * th = new QThread();
mythread->moveToThread(th);
QObject::connect(th,SIGNAL(started()),mythread,SLOT(OnStarted()));
QObject::connect(th,SIGNAL(finished()),mythread,SLOT(OnFinished()));
th->start();
Your initialization and termination tasks in the class Newton should be done in OnStarted() and OnFinished() slots respectively.
Now you can implement a slot in your worker thread which runs the calculations. You can connect a signal in your GUI thread to that slot which contains the arguments you want to send. When emitting that signal along the arguments, the calculation slot in the working thread is started with the appropriate arguments.