I am trying to learn QThread in Qt. I wrote following code for QThread which is working quite good.
QThread* mThread = new QThread;
FaceCutThread* mFaceCut = new FaceCutThread();
mFaceCut->moveToThread(mThread);
connect(mThread, SIGNAL(finished()), mFaceCut, SLOT(deleteLater()));
connect(this, SIGNAL(operateFaceCut(std::string)), mFaceCut, SLOT(processFaceCut(std::string)));
connect(mFaceCut, SIGNAL(isFinisedFaceCut(QImage,bool)), this, SLOT(handleFaceCutResults(QImage,bool)));
mThread->start();
Now I want to add one more class which should work as a QThread, like;
Enroll *mEnroll = new Enroll();
Should I use mEnroll object with previous mThread or should I create new mThread2;
QThread* mThread2 = new QThread;
mEnroll->moveToThread(mThread2);
What's the advantages and disadvantages?
It depends what do you want to achieve. If you put the same classes in the same thread then they will be executed in the same thread. If you want them to be executed in separate threads then put to another thread. For example if you want to use them independently - one thread loading/reading data, second processing data and connecting them via signals. Separate threads creates problem with synchronization if threads shares resources etc. ( long topic http://www.drdobbs.com/tools/avoiding-classic-threading-problems/231000499 ) Having objects in the same thread means that objects have common
Register state (including PC and stack pointer)
Stack
Signal mask
Priority
Thread-private storage
And this can be treated as advantage or disadvantage ...
Related
I am using Ubuntu 12.04 with Qt version 4.8.3.
In Qt main window I manage to open my devices and this part of the code is working.
Now after I open devices I need to wait if a card is present. It means I have to use polling to get the data from card. But the polling must be infinite loop. Polling for card arrival and removal.
Example poll for card arrival every 20ms and when a card is detected I need to switch poll for card removal every 20 ms. So when a card arrival or removal is detected, my app signals Qt event such that another Qt thread can now proceed to read/write the card.
I read about QThread, mutex locked so on and I am bit confused.
I have a main window plus a worker class. In my mainwindow I code as;
// Open a reader (from my SDK)
cReader.open
//If the reader is open use;
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(worker,SIGNAL(??????),SLOT(?????);
connect(worker,SIGNAL(?????),SLOT(?????);
.........
First I must use connect for SIGNAL/SLOT and start the card arrival/removal polling. Than if any card detected I have signal to another thread to read from card or write into the card.
So I don’t know where to start or how to call signal/slot? I need help to fill the ?????? above SIGNAL/SLOT.
Edited: I also need mutex lock shared by the polling thread and the card handiling thread. This is because a card poll command will invalidate my mifare session if opened.
Any help please,
Kind Regards,
Using QMutex for protection an object, on the example of your cReader:
// class member
QMutex m_mutex;
//...
QByteArray MyClass::safeReadSomeData()
{
m_mutex.lock();
QByteArray result = cReader.read();
m_mutex.unlock();
return result;
}
See also QMutexLocker, QReadWriteLock.
Common and usual way for communication and parameters exchange between threads is using signals & slots. Example:
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect( thread, SIGNAL(started()), worker, SLOT(startMyWork()) );
connect( worker, SIGNAL(sigCardDetected()), someOtherObject, SLOT(onCardDetected()) );
thread->start();
//...
Useful article from official documentation: Threads and QObjects
Also I think this answer about QThread will be useful for you: https://stackoverflow.com/a/35056527/4149835
p.s. Are you sure that you need to use two different additional threads for detecting and reading/writing?
It is not thread-safe (unless one QObject accesses data in another QObject and both belong to the same thread).
In terms of usage of mutexes the QMutexLocker is your friend and I advise you to use it instead of manually handling the locking/unlocking.
If you re-read your question you will notice that you heavily use intervals. And how do we handle intervals? Using QTimer. Here is my suggestion:
Create a QTimer along with the worker QObject
Set the interval of the timer to 20ms or whatever interval you want for it to tigger an event; a timer with interval set to 0 means that an event will be triggered by it as soon as possible
Connect the timer to the slot of the worker that does the work (check if card is removed etc.)
Connect the object's slots/signals to the signals/slots of your UI (using QMutexLocker will enable you to secure the access on the internal data of both) or another QObject (in the same or a different thread where the worker is residing)
Move both the timer and worker to a QThread and start the thread
The timer will start triggering a check for your card every X milliseconds. The worker will then receive that signal from the timer (here no mutex is required since both the timer and the worker are with the same thread-affinity). Things will change internally for the worker and then it will emit a signal to another QObject or the UI itself. At this point the mutexes come into play unless you are accessing another instance of a QObject in the same thread where your worker is.
You can add as many threads as you like by doing so. I have a UI that has 6 threads running in the background accessing both the UI and each other without any problem using timers.
EDIT:
I have started working on a small demo using QTimer, QThread and QObject. Application is incomplete/buggy but you can see how QTimer works.
I have to execute some heavy code in background thread by timeout. And I do not want to subclass QThread for every such workers. Is this a proper way?
/* inside QObject subclass */
auto thread = new QThread(this);
auto timer = new QTimer(nullptr);
timer->moveToThread(thread);
timer->setInterval(1000);
connect(timer, &QTimer::timeout, [](){
/* do lambda work */
});
connect(thread, SIGNAL(started()), timer, SLOT(start()));
connect(thread, &QThread::destroyed, timer, &QTimer::deleteLater);
thread->start();
Initially the code presented looks ok. However, it depends on what you plan to do in the lambda function and what objects you're going to use and where they reside.
Your lambda function doesn't capture any variables. If this is intended, then it should be fine. However, if you're planning on using objects which have already been instantiated on the main thread, you'll have to think carefully about their thread affinity (which thread they're running on) when you try to use them in the lambda function.
Personally, I'd create a separate object, derived from QObject, which creates the QTimer and lambda function, then move that object to the new thread. Communication between this object and those on the main thread is performed via signal and slots.
I am looking at some github projects, where one of them did the UDPlink in the following way,
first it subclass QThread to create a class UDPLink:public QThread
and its constructor and deconstructor is like:
UDPLink::UDPLink(UDPConfiguration* config)
: _socket(NULL)
, _connectState(false)
{
Q_ASSERT(config != NULL);
_config = config;
_config->setLink(this);
// We're doing it wrong - because the Qt folks got the API wrong:
// http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
moveToThread(this);
// Set unique ID and add link to the list of links
_id = getNextLinkId();
qDebug() << "UDP Created " << _config->name();
}
UDPLink::~UDPLink()
{
// Disconnect link from configuration
_config->setLink(NULL);
_disconnect();
// Tell the thread to exit
quit();
// Wait for it to exit
wait();
this->deleteLater();
}
Though the code did compile and work, but I wonder whether this way of using a QThread would be correct?
The Qt docs for QThread describe the two ways threading can be done with QThread. Sub-classing QThread was the only way to use QThread initially. To use QThread in this manner, override the run method, which is the QThread method that runs on a new thread. QThread should be thought of as a thread manager, not an object that runs on a separate thread itself. From the docs:
It is important to remember that a QThread instance lives in the old
thread that instantiated it, not in the new thread that calls run().
This means that all of QThread's queued slots will execute in the old
thread. Thus, a developer who wishes to invoke slots in the new thread
must use the worker-object approach; new slots should not be
implemented directly into a subclassed QThread.
When subclassing QThread, keep in mind that the constructor executes
in the old thread while run() executes in the new thread. If a member
variable is accessed from both functions, then the variable is
accessed from two different threads. Check that it is safe to do so.
QThread documentation page
It's because QThread is a thread manager class that a solution for moving objects to threads was created. The comment in the code you provided makes a statement about this change, since that article states that moveToThread(this) isn't a good practice.
Creating an object and moving it to a thread and sub-classing QThread are both valid approaches to threading with Qt, as the documentation now states clearly. There is a benefit to using the worker-object approach, if you desire to use signal/slot connections across thread boundaries: a worker object will have its slots available on the thread it is moved to.
As Qt developer recommended, Code you mentioned is not correct way to use QThread.
Recommended way is suggested here.
Sample code from Post.
Producer producer;
Consumer consumer;
producer.connect(&consumer, SIGNAL(consumed()), SLOT(produce()));
consumer.connect(&producer, SIGNAL(produced(QByteArray *)), SLOT(consume(QByteArray *)));
// they both get their own thread
QThread producerThread;
producer.moveToThread(&producerThread);
QThread consumerThread;
consumer.moveToThread(&consumerThread);
// go!
producerThread.start();
consumerThread.start();
I have a class ParentThread deriving from QThread with the following run() method that roughly looks as follows:
void ParentThread::run()
{
QThread *childThread = new QThread;
QObject::connect(childThread, SIGNAL(finished()), this, SLOT(onChildThreadFinished());
QObject::connect(childThread, SIGNAL(finished()), childThread, SLOT(deleteLater());
childThread->start();
exec();
}
The slot onChildThreadFinished() is defined on ParentThread, and should run in the context of ParentThread. However, using the code above, onChildThreadFinished only gets called in case the signal/slot connection is a Qt::DirectConnection, but then runs in the context of the child thread. In case the signal/slot connection is defined as a Qt::QueuedConnection, the slot never gets called. I am using Qt 4.8.5. Any idea what the issue is here?
You state that The slot onChildThreadFinished() is defined on ParentThread, and should run in the context of ParentThread. This assumption is wrong. This is one of the reasons why subclassing QThread is discouraged.
If you want to use slots in your threads, subclassing QThread is not what you want to do. Use worker-object method instead. Subclass QObject and call QObject::moveToThread to move your object to a new thread.
Here is what Qt docs say about this:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
In order to ease my cleanup efforts, I want to set the parent of my worker object to be the Qthread to which it is moved. (See below).
void TelnetServer::incomingConnection(qintptr socketDescriptor)
{
QThread * TelnetConnectionThread = new QThread(this);
TelnetConnection *worker = new TelnetConnection(socketDescriptor,TelnetConnectionThread);
connect(TelnetConnectionThread, SIGNAL(started()), worker, SLOT(start()));
connect(TelnetConnectionThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
worker->moveToThread(TelnetConnectionThread); // Move worker into QThread
TelnetConnectionThread->start();
}
Right before the start() line, I added:
worker->setParent(TelnetConnectionThread);
but at runtime I see an error that I can't do that because the new parent is in a different thread. How can that be? In the line above I moved the worker to the new thread...so the worker should be in the same thread as the TelnetConnectionThread. Help?
I confirmed with some qDebug's and thread() that the worker does in fact get moved to the new thread!
I think you're a bit confused about QThread. The first problem is that its name is rather misleading as it's not actually a thread, but a thread controller. Next is the issue of thread affinity (the thread that an object is actually running on).
If we start in the main thread and create a new QThread, the thread controller is instantiated in the main thread: -
QThread* pThread = new QThread;
Next the thread is started: -
pThread->start();
Even though pThread is thought to be running in a different thread, its thread affinity is still the main thread, but any QObject-based class instance that gets moved to pThread will have the thread affinity of the new thread: -
QObject* pObject = new QObject;
pObject->moveToThread(pThread);
Still, pThread's thread affinity is the main thread, while pObject's thread affinity is the new thread; remember, pThread is actually a thread controller!
To set the parent of pObject to be pThread would be wrong, as they have different thread affinity. This is a problem that many people encounter when trying to inherit from QThread, rather than using it as a separate entity and moving QObjects to it. What usually happens is that objects are sometimes created in the constructor of their inherited QThread class, without parenting them and not realising that those objects will have the thread affinity of the main thread, not the new thread as they expect.
Moving a QObject to another thread also moves its children, so trying to set the parent as the thread (thread controller!) does not make sense.
To summarise, you cannot set the parent of your worker object to be the TelnetConnectionThread, as they run on different threads.
However, if you're trying to have the thread cleanup after itself when finished, you can do this: -
connect(TelnetConnectionThread, SIGNAL(finished()), TelnetConnectionThread, SLOT(deleteLater()));