QTimer in worker thread - c++

I have a Worker class which does some work in another thread, where it's placed using moveToThread(). Inside Worker::doWork() method I also create a QTimer which is supposed to emit progress updates, based on the estimated time the task will take.
Here's an example:
ThreadController::ThreadController()
{
Worker* worker = new Worker;
worker->moveToThread(&m_workerThread);
// ...
m_workerThread.start();
emit startWorker(params); // connected to Worker::doWork()
}
class Worker : public QObject
{
Q_OBJECT
public slots:
Worker::doWork(const QString& params)
{
QTimer* timer = new QTimer(this);
connect( timer, SIGNAL(timeout)), this, SLOT(updateProgress()) );
timer->start(estimateTaskLength() / 100);
// perform work...
}
}
Now this doesn't work as intended. The updateProgress() slot only starts being called AFTER the work has been completed.

When the timer expires, the timeout event is queued to the event queue of your worker QThread. However, your worker QThread is busy executing doWork(), so the event can't be processed. Once the thread completes the doWork, control returns the the QThread's event loop and the timeout event is executed.
The simplest approach to fixing this would be to manually invoke the event loop using QCoreApplication::processEvents() at certain intervals throughout the execution of doWork(). This will allow the QThread to execute the timeout event earlier.
Alternative, you can use a different thread to perform these estimations. If they are not too performance-intensive, you can even use the GUI/main thread. The GUI/main thread is expected to remain "responsive" to events (otherwise the application appears to hang) so it can process the timeout event in a timely manner.

This probably depends on the type of connection between Qtimer::timeout and this::updateProgress.
By default, its a Qt::AutoConnection, which means that the timeout signal will be queued until the receiving object is ready to handle it. I.e. after doWork is finished.
If you connect using
connect( timer, SIGNAL(timeout)), this, SLOT(updateProgress()), Qt::DirectConnection );
the timeout signal should be treated immediately. (Make sure to include necessary mutexes and stuff though, as this is a typical case where e.g. concurrent accesses may occur..)
( http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum )

Related

How to wait for QThread eventDispatcher being ready?

I'm using QThread's event loop to communicate with worker threads.
I noticed that I can't use the thread's event loop just after a start.
The following code does not work, as threadEventDispatcher appears to be NULL.
QThread *thread = new QThread;
thread->start();
auto* threadEventDispatcher = thread->eventDispatcher();
Q_ASSERT(threadEventDispatcher != nullptr);
QMetaObject::invokeMethod(threadEventDispatcher, [] { printf("Hello world\n");
According to Qt doc,
An event dispatcher is automatically created for the main thread when QCoreApplication is instantiated and on start() for auxiliary threads.
By browsing the QThread source code, I'm under the impression that the eventDispatcher is created in the running thread, which may not yet be ready when start exits.
I managed to access the eventDispatcher by using a sleep after the start, but I could not find an API that would allow me to properly wait for the thread to actually run.
How can I wait for the thread to actually run, and access eventDispatcher ?
Simply connect to QThread::started() signal.

Executing two thread in parallel with one thread containing an event

I have two functions which has to be executed after specific interval of time. So I create two threads and each function is executed in separate threads. One of the function "checkForEvent()" has a Event which is waiting for an event to happen.
When the "checkForEvent()" function is waiting for an event the other function "checkServer()" is not being executed even though it is run in a separate thread.
dongleThread = new QThread(this);
checkDongle = new QTimer();
checkDongle->setInterval(DONGLE_CHECK_TIMER);
checkDongle->moveToThread(dongleThread);
connect(checkDongle, SIGNAL(timeout()), this, SLOT(checkForEvent()));
connect(dongleThread, SIGNAL(started()), checkDongle, SLOT(start()));
dongleThread->start();
serverThread = new QThread(this);
checkServer = new QTimer();
checkServer->setInterval(SERVER_CHECK_TIMER);
checkServer->moveToThread(serverThread);
connect(checkServer, SIGNAL(timeout()), this, SLOT(checkServer()));
connect(serverThread, SIGNAL(started()), checkServer, SLOT(start()));
serverThread->start();
checkForEvent() //This function is waiting for an Event
checkServer()
How can I execute both the functions in separate threads ?
Thank you!!
Your connect call uses the default connection type: Qt::AutoConnection. Here is what this means:
Qt::AutoConnection: If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Here your QTimer and this are in a different thread. It means Qt::QueuedConnection will be used.
Qt::QueuedConnection: The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
So your two slots are called in the thread of this. If one is blocking, the other one will never run.
One way of fixing the problem is having a worker object moved to its own thread. The worker object would call checkForEvent and emit a signal after the wait is over. This signal can be connected to checkServer in this. With this, you need only one thread. You can look at the documentation of QThread for more information about worker objects.

Qt: Sending signal to dead/stopped thread

I've come across the problem, that I might be sending signals from one QThread to another, however I have no way of checking if the thread that is implementing the slot is running. How will the framework handle such a situation while using Qt::QueuedConnection ?
...
WorkerImp* pWorker = new WorkerImp();
QThread worker;
pWorker->moveToThread(&worker);
QObject::connect(&worker, QThread::finished, pWorker, &QObject::deleteLater, Qt::QueuedConnection);
bool connected = QObject::connect(this, &SlaveMaster::requireWork, pWorker, &WorkerImp::doWork, Qt::QueuedConnection);
assert(connected);
// at this point we have connected the signal, thread is not starded.
// however the object that we use to connect still exists and will after we exit the thread.
worker.start();
worker.exit();
worker.wait();
// note that when we exit the QThread we do not destroy the object - it can be start over
emit requireWork();
...
The signal is never handled. A queued connection across threads is posted as an event to the thread, handled by its event loop. If the thread is stopped (and thus its event loop), there is no one to pick up the event and deliver it:
From the Qt Docs Signals and Slots Across Threads:
Queued Connection: The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
and Per-Thread Event Loop:
[...] If no event loop is running, events won't be delivered to the object.
Note that a blocking queued connection would dead-lock

Queue several QTimers calls to slots

I'm using a library where I need to call a trigger function that starts some processing (starts a thread that does the job) and returns immediatly. Then when the processing is finished a signal is emitted.
This needs to be done periodically based on different parameters. Since other calls to the trigger function must not be done while processing I would need to queue them in some way. I thought about using a QEventLoop ("loop") but without luck so far.
Please look at this piece of code:
test::test()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(timerSlot()));
connect(&timer2, SIGNAL(timeout()), this, SLOT(timer2Slot()));
connect(&library, SIGNAL(processingFinished()), &loop, SLOT(quit()));
timer.setInterval(2000);
timer.start();
timer2.setInterval(4000);
timer2.start();
}
void test::timerSlot()
{
loop.exec();
startProcessing(some_parameters);
}
void test::timer2Slot()
{
loop.exec();
startProcessing(some_other_parameters);
}
The issue is that when loop.exec() is called while processing I get the message:
QEventLoop::exec: instance xxxxxx has already called exec()
What would be the right way to do what I intend?
Thanks in advance.
One simple solution is to introduce a member variable, for example, bool m_isProcessing, start processing only if m_isProcess == false, then set it to true when you start processing and reset it to false when processing is complete. Since the slots for your test QObject execute on the GUI/main thread, you don't need to worry about synchronization between timer slots and the slot that will execute when processing is finished.
If you want to keep track of events that occur during processing, you can use the same method: introduce a member variable to the test class to track the information you need.
It seems that you are looking for Qt::QueuedConnection.
Qt::QueuedConnection 2 The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
Therefore, you could write something like this:
connect(&timer, SIGNAL(timeout()), SLOT(timerSlot()), Qt::QueuedConnection);
connect(&timer2, SIGNAL(timeout()), SLOT(timer2Slot()), Qt::QueuedConnection);
For details, you could look into the well-known mandelbrot example how this is done in there, although it is using worker threads:
Mandelbrot Example

Knowing when a QThread's event loop has started from another thread

in my program, I am subclassing QThread, and I implemented the virtual method run() like so:
void ManagerThread::run() {
// do a bunch of stuff,
// create some objects that should be handled by this thread
// connect a few signals/slots on the objects using QueuedConnection
this->exec(); // start event loop
}
Now, in another thread (let's call it MainThread), I start the ManagerThread and wait for its started() signal, after which I proceed to use the signals and slots that should be handled by ManagerThread. However, the started() signal is essentially emmitted right before run() is called, so depending on thread scheduling I lose some signals from MainThread, because the event loop hasn't started yet!
(EDIT: turns out that's not the problem, it's just the signals are not connected in time, but for the same reason)
I could emit a signal right before calling exec(), but that's also asking for trouble.
Is there any definitive/simple way of knowing that the event loop has started?
Thanks!
EDIT2:(SOLUTION)
Alright, so it turns out the problem isn't exactly what I phrased. The fact that the event loop hasn't started isn't the problem, since signals should get queued up until it does start. The problem is, some of the signals would not get connected in time to be called- since the started() signal is emitted before run() is called.
The solution is to emit another custom signal after all the connections and right before exec. That way all signals/slots are ensured to be connected.
This is the solution to my problem, but not really an answer to the thread title. I have accepted the answer that does answer the title.
I have left all my code below for those curious, with the solution being, to wait for another signal in the instance() method.
CODE:
Many of you are saying that I cannot lose signals, so here is my whole class implementation. I will simplify it to just the bare necessities.
Here is the interface to ManagerThread:
// singleton class
class ManagerThread: public QThread {
Q_OBJECT
// trivial private constructor/destructor
public:
static ManagerThread* instance();
// called from another thread
public:
void doSomething(QString const& text);
// emitted by doSomething,
// connected to JobHandler whose affinity is this thread.
signals:
void requestSomething(QString const& text);
// reimplemented virtual functions of QThread
public:
void run();
private:
static QMutex s_creationMutex;
static ManagerThread* s_instance;
JobHandler* m_handler; // actually handles the requests
};
Some relevant implementations. Creating the singleton instance of the thread:
ManagerThread* ManagerThread::instance() {
QMutexLocker locker(&s_creationMutex);
if (!s_instance) {
// start socket manager thread, and wait for it to finish starting
s_instance = new ManagerThread();
// SignalWaiter essentially does what is outlined here:
// http://stackoverflow.com/questions/3052192/waiting-for-a-signal
SignalWaiter waiter(s_instance, SIGNAL(started()));
s_instance->start(QThread::LowPriority);
qDebug() << "Waiting for ManagerThread to start";
waiter.wait();
qDebug() << "Finished waiting for ManagerThread thread to start.";
}
return s_instance;
}
Reimplementation of run that sets up signals/slots and starts event loop:
void ManagerThread::run() {
// we are now in the ManagerThread thread, so create the handler
m_handler = new JobHandler();
// connect signals/slots
QObject::connect(this,
SIGNAL(requestSomething(QString const&)),
m_handler,
SLOT(handleSomething(QString const&)),
Qt::QueuedConnection);
qDebug() << "Starting Event Loop in ManagerThread";
// SOLUTION: Emit signal here and wait for this one instead of started()
this->exec(); // start event loop
}
Function that delegates the handling to the correct thread. This is where
I emit the signal that is lost:
void ManagerThread::doSomething(QString const& text) {
qDebug() << "ManagerThread attempting to do something";
// if calling from another thread, have to emit signal
if (QThread::currentThread() != this) {
// I put this sleep here to demonstrate the problem
// If it is removed there is a large chance the event loop
// will not start up in time to handle the subsequent signal
QThread::msleep(2000);
emit(requestSomething(text));
} else {
// just call directly if we are already in the correct thread
m_handler->handleSomething(text);
}
}
Finally, here is the code from MainThread that will fail if the event loop doesn't start in time:
ManagerThread::instance()->doSomething("BLAM!");
Assuming that the handler just prints out its text, here is what gets printed out on a successful run:
Waiting for ManagerThread to start
Finished waiting for ManagerThread thread to start.
Starting Event Loop in ManagerThread
ManagerThread attempting to do something
BLAM!
And here is what happens on an unsuccessful run:
Waiting for ManagerThread to start
Finished waiting for ManagerThread thread to start.
ManagerThread attempting to do something
Starting Event Loop in ManagerThread
Clearly the event loop started after the signal was emitted, and BLAM never prints.
There is a race condition here, that requires the knowledge of when the event loop starts,
in order to fix it.
Maybe I'm missing something, and the problem is something different...
Thanks so much if you actually read all that! Phew!
If you setup the connections right, you shouldn't be losing the signals. But if you really want to get a notice on the start of the thread's event loop, you can try QTimer::singleShot() in your run() right before calling exec(). It will be delivered when the event loop starts and only delivered once.
You could look at QSemaphore to signal between threads. Slots and signals are better for ui events and callbacks on the same thread.
Edit: Alternately you could combine QMutex with QWaitCondition if a semaphore is not applicable. More example code to see how you are using the ManagerThread in conjunction with the MainThread would be helpful.
This is a non-issue. Signals between threads are queued (more specifically, you need to set them up to be queued in the connect() call because direct connections between threads aren't safe).
http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads
You could create the signal/slots connections in the constructor of the ManagerThread. In that way, they are certainly connected even before run() is called.