qt signal-slot multithreading deadlocks - c++

In my code a worker thread emits a signal to gui thread. Signal and Slot are connected via Qt::BlockingQueuedConnection.
When during thecloseEvent application is trying to stop the worker thread and only then finishes the event.
It calls the function of worker thread:
void stop() {
_isFinished = true;
wait();
}
The worker thread in its turn checks _isFinished condition and if it is not set up then emit the signal.
Imaging the following situation
The worker Gui Thread
================= =================
if (!_isFinished) -----------------
----------------- _isFinished = true;
----------------- wait();
emit mySignal(); -----------------
Evidently, both threads would be locked - deadlock.
If I add a mutex then you can find another deadlock situation. When the worker locks the mutex and the gui thread would be blocked. As a result the emitted signal will lock the worker.
How can I deal with this?

This is how I see the situation.
In your main thread you do not need to wait for worker to quit. Instead your closeEvent should look like this:
if (workerThread->isRunning())
{
workerThread->stop();
event->ignore();
}
To make this work, you also need to close main window, when worker thread finishes:
connect(workerThread, SIGNAL(finished()), SLOT(close()));
Finally, all manipulations with _isFinished (and all other variables in worker thread) should occur in worker thread. You can achieve it with a simple trick:
WorkerThread.h
public:
void stop();
protected slots:
void p_stop();
protected:
QMutex mutex;
WorkerThread.cpp
void WorkerThread::stop()
{
staticMetaObject.invokeMethod(this, "p_stop", Qt::QueuedConnection);
}
void WorkerThread::p_stop();
{
QMutexLocker locker(&mutex);
_isFinished = true;
}
This way you can avoid deadlocks.

In the gui thread you can use QEventLoop for wait. Look like this:
void stop() {
QEventLoop eventloop;
connect(&theWorkerThread, &QThread::finished, &eventloop, QEventLoop::quit);
_isFinished = true;
eventloop.exe();
}

Related

Why is std::thread blocking execution?

I want to have a class method running in a separate thread :
std::thread myThread(&Class::method, this);
myThread.join();
//Other events
While doing this, the other events occur only when the Class:method is over, not simultaneously.
What did I forget ?
You are calling .join() on the thread, which blocks until that thread has finished, to run things concurrently with that thread either call join() after everything else that you want to run concurrently has finished, or call detach() on the thread object instead
For example
auto th = std::thread{[]() { ... }};
do_something();
th.join();
Here in this example do_something() will run concurrently with the thread th, or you can call detach()
std::thread{[]() { ... }}.detach();
do_something();
This is what happens:
Start a thread and have it run &Class::method
std::thread myThread(&Class::method, this);
Wait until the thread has ended.
myThread.join();
Do other things in the curren thread
//Other events
As you can see, your myThread.join() pauses your current thread.
Do it like this instead:
std::thread myThread(&Class::method, this);
//Other events
myThread.join();
Alternatively; don't perform that join and call myThread.detach(); instead.

How to unpause thread in Qt

I want to know if there is a way to unpause a sleeping thread in Qt
I pause the thread using QThread::msleep(ms) function
And I want to unpause the thread before time runs out
Is there any way to do it ?
First of all, If you are using QSerialPort, you really don't have to mess up with threads. You should deal with it asynchronously. So, you read() in slots connected to readyRead() signal, and if you want to have some delay between writes use a QTimer and write() in slots connected to its timeout() signal as suggested by #deW1's comment.
If you really want to go with multiple threads, #sploid's code approach has many mistakes:
A mutex has to be unlocked only by the thread that locked it. Here is what the documentation for QMutex::unlock() says:
Unlocks the mutex. Attempting to unlock a mutex in a different thread to the one that locked it results in an error. Unlocking a mutex that is not locked results in undefined behavior.
A QMutex is used to protect an object, data structure or section of code so that only one thread can access it at a time. It is not used for a thread to signal some other thread.
A QWaitCondition allows a thread to tell other threads that some sort of condition has been met. This is what you need to use to tell the other thread to "unpause".
Here is how it should have been done:
class Thread : public QThread{
public:
Thread(QObject* parent=nullptr):QThread(parent){
}
~Thread(){}
void InterruptWaitState(){
QMutexLocker locker(&mutex);
cond.wakeAll();
}
protected:
void run(){
//waiting thread
QMutexLocker locker(&mutex);
if(cond.wait(&mutex, 5000)){
// unlock from another place
} else {
// exit by timeout
}
}
private:
QMutex mutex;
QWaitCondition cond;
};
QMutex mut;
void InterruptMethod() {
mut.unlock();
}
void SleepMethod() {
mut.lock();
const int kTimeout = 5000;
bool over_event_come = mut.tryLock(kTimeout); // here was sleep
if (over_event_come) {
// unlock from another place
} else {
// exit by timeout
}
mut.unlock();
}

Qt thread design producer consumer

The main thread is loading tasks into a queue.
A worker thread is dealing with these tasks.
My code is like this:
//Core subclass a QThread
Core::AddTask()
{ ...
mutex.lock();
tasks.append(task);
mutex.unlock();
start();//which calls the run function
}
Core::RefreshTask()
{ ...
mutex.lock();
tasks.clear();
mutex.unlock();
// Calculate the new tasks...
...
//foreach newly added task call
AddTask();
}
Core::run()
{ ...
while (1)
{
finish = false;
mutex.lock();
tasks.dequeue();
if (tasks.size() == 0)
finish = true;
mutex.unlock();
...
if (finish)
break;
}
}
However I found the worker thread failed to finish all the tasks because when the run function is being processed, it will not response to start() call.
Then in the situation that: the run function is processing the last task and in the sametime AddTask is being called, then its start() call will do nothing. And the run() function finish and ignore the task.
I know a signal/slot mechanism could solve the problem. But I am forced to use the old QThread::run() style multithreading...
Any suggestions how to properly write a producer consumer pattern in Qt?
I think you don't need call start() in Core::AddTask(). When you add new task to task list, you can send some kind of event message. It can be Qt signal, condition variable or something else.
Event loop works in separate thread and process tasks. If task list is empty, event loop waits for an event.

std::thread consumes all CPUs in Qt

I have a function. In my function there is a c++ thread & a Qtimer. By c++ thread I receive ARP Reply packets & by QTimer I send ARP Request packets.
The simplified structure:
int foo()
{
... some codes ...
QTimer::singleShot(1000, this, SLOT(beginSending()));
std::thread tCapture(Capture);
tCapture.join();
return 0;
}
void Capture()
{
while ( ! finishCapturing )
{
do sth
}
}
In the tCapture thread I have a while loop that consumes all CPUs & the Qtimer does not work!
I use .join() because I want to wait for the thread to finish.
when I set finishCapturing flag in Qtimer slot, the thread will be finished.
The above codes don't work correctly, because the c++ thread consumes all CPUs!
what is the problem?
Thanks a lot.
Ya Ali.
The problem is joining the thread right after creating them, that blocks the GUI thread and the QTimer and all slots on that thread.
What you should do is emit a signal when capturing is done;
public void beginSending(){
//do sending and capture
finishCapturing =true;
emit finshedCapture();
}
If needed you can put the body of the while in a slot and have it called repeatedly with a QTimer with a timeout of 0 (which means the slot will get called as often as possible).
then you can connect the finshedCapture() signal to the stop() slot of the QTimer
int foo()
{
... some codes ...
QTimer::singleShot(1000, this, SLOT(beginSending()));
QTimer* timer = new QTimer(this);
connect(timer, signal(timeout()), this, slot(Capture()));
connect(this, signal(finshedCapture()), timer, slot(stop()));
connect(this, signal(finshedCapture()), timer, slot(deleteLater()));//cleaup when done
timer->setTimeout(0);
timer->setSingleShot(false);
timer->start();
return 0;
}
void Capture()
{
//no while because the timer will call it as needed
//do sth
}
Without seeing more of the code, this is guess-work:
1: Capture() does not block, so will consume as much CPU time as the operating system gives it - which will be 100% of one CPU on a lightly loaded multi-core system.
2: foo() is called on the main thread. It then attempts to join() with the your thread - which blocks until the thread finished. I see nothing setting finishCapturing, so it doesn't.
3: The QTimer is dispatched through the run-loop - which is blocked.
In effect, this is deadlock of the main thread.

Unable to connect between QThread with finished() signal for multiple Qthread

I have a 3 QThreads invoking by one another (all inherited from QThread. I know some might suggest to use moveToThread, but just ignore this fact for now). The simplified code looks like following:
Thread1 class:
void
Thread1::run
{
// some execution
Thread2 t2 = new Thread2();
connect(t2,SIGNAL(finished),this,SLOT(onFinished));
t2->start();
while(!stop) // stop was initialized as false
{
this->msleep(10);
}
}
void Thread1::onFinished(){ stop = true; }
Thread2 class:
void
Thread2::run
{
// some execution
Thread3 t3 = new Thread3();
connect(t3,SIGNAL(finished),this,SLOT(onFinished));
t3->start();
while(!stop) // stop was initialized as false
{
this->msleep(10);
}
}
void Thread2::onFinished(){ stop = true; }
Thread3 class:
void
Thread3::run
{
// some execution
QMutexLocker ml(&mMutex);
}
When I have only two threads, it works perfectly fine (e.g. just thread2 and thread3). The onFinished() method seems not connecting with finished() signal properly anymore, after I moved to a three-threads scenario. The onFinished() in thread2 has ever been called. And I am pretty sure the execution of the thread3 has completed.
Can anybody tell me where I could have done wrong?
First of all you should note that the default connection type is Qt::AutoConnection. This means if signal is emitted from a different thread than the receiving object's thread, Qt::QueuedConnection is used. In this case: 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 you need an event loop.
It works with 2 threads because you probably have an event loop running in your main thread. In your case where you use only thread2 and thread3 objects, thread2 object will actually live in the main thread, while thread3 object will live in the thread managed by the thread2 object. So slots in thread2 object should work.
But in the case of 3 threads, thread1 object would live in the main thread, thread2 object would live in the thread managed by thread1 object, and because there is no running event loop there, the slot in thread2 object will never be executed.
You can call QThread::exec() in your QThread::run() function, but note that the slots will be executed in the thread where your QThread object lives in, not the thread it manages. Because of this you shouldn't use slots in QThread subclasses. You should create a QObject subclass and move it to a thread.
Another option is to use Qt::DirectConnection for the connection type, when you connect your signals to slots.