I have some code use qtcpsocket to write and read, write-->sleep-->read; and ui had 2 and more timer to use this function; by i want i run synchronous;so i add mutex to lock it; by it deadlock;
qt4; qt5;
void MainWindow::Start()
{
pTimer = new QTimer(this);
pTimer->setInterval(100);
connect(pTimer,SIGNAL(timeout()), this, SLOT(OnTimer()) );
pTimer->start();
pTimer2 = new QTimer(this);
pTimer2->setInterval(100);
connect(pTimer2,SIGNAL(timeout()), this, SLOT(OnTimer()) );
pTimer2->start();
}
void MainWindow::OnTimer()
{
FunCal(); // in my real code it will MyObj.DoSometing();
}
void MainWindow::FunCal()
{
qDebug()<<"log in fun...";
QMutexLocker loc(&this->_mtx);
qDebug()<<"getted lock in fun...";
QEventLoop loop;
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
qDebug()<<"log out fun...";
}
I Want i run and out put as: log in fun... getted lock in fun ... log out fun... log in fun... getted lock in fun... log out fun...
but It Run like this: log in fun... getted lock in fun ... log in fun.... ---------------------------------no more ---------------------
IMHO the issue of OP results from a basic misunderstanding:
QTimerdoesn't introduce multithreading.It's just a facility to queue events which will sent after a certain time.
That's why, the
QEventLoopis necessary to make it running at all.However, it's still a deterministic execution and this is what probably happens inside the code of OP:
pTimer->start();→ starts first timerpTimer2->start();→ starts second timerQApplication(not exposed in code)MainWindow::FunCal()qDebug()<<"log in fun...";→ output oflog in fun...QMutexLocker loc(&this->_mtx);→this->_mtxbecomes lockedqDebug()<<"getted lock in fun...";→ output ofgetted lock in fun...loop.exec();→ enter a nested event loop (Nested event loops are allowed in Qt.)MainWindow::FunCal()(Please, remember that it was immediately started after first with same interval time.)qDebug()<<"log in fun...";→ output oflog in fun...QMutexLocker loc(&this->_mtx);→ PROBLEM!To illustrate it further, imagine the following call stack at this point (above called below):
(Note: In reality, you will see much more entries in the call-stack which I considered as irrelevant details in this case.)
The problem is: This second call of
MainWindow::FunCal()cannot lock the mutex because it is already locked. Hence, the execution is suspended until the mutex is unlocked but this will never happen. The locking of mutex happened in the same thread (in the first/outer call ofMainWindow::FunCal()). Unlocking would require to return from this point but it cannot because it's suspended due to the locked mutex.If you think this sounds like a cat byting into its own tail – yes, this impression is right. However, the official term is Deadlock.
The usage of a
QMutexdoesn't make much sense as long as there are no competing threads. In a single thread, a simpleboolvariable would do as well because there are no concurrent accesses possible in a single thread.Whatever OP tried to achieve in this code: Concerning the event-based programming forced/required by Qt, the problem is simply modeled wrong.
In single threading, a function cannot be entered twice accept by
Leaving the 2. aside (irrelevant for OPs Qt issue), the recursive call happens explicitly due to establishing a second (nested) event loop. Without this, the whole (mutex) locking is unnecessary and should be removed as well.
To understand event-based programming in general – it's described in the Qt doc. The Event System.
Additionally, I found Another Look at Events by Jasmin Blanchette which IMHO gives a nice little introduction into how the Qt event-based execution works.
Note:
Event-based programming can become confusing as soon as the amount of involved objects and signals becomes large enough. While debugging my Qt applications, I noticed from time to time recursions which I didn't expect.
A simple example: A value is changed and emits a signal. One of the slots updates a Qt widget which emits a signal about modification. One of the slots updates the value. Hence, the value is changed and emits a signal...
To break such infinite recursions, a
std::lock_guardmight be used with a simple DIYclass Lock:A sample object with
bool _valuestd::function<void()> sigValueSetsetValue():Lock _lockValueFinally, some code to force the lock into action:
To keep things short, I connected a slot to the signal which directly sets value again while the signal is emitted.
Output:
Live Demo on coliru
For a counter-example, I excluded the test
if (_lockValue) return;and got the following output:Live Demo on coliru
This is similar to what happened in OPs case with the only difference that in my case double-locking just violated the
assert().To make this complete, I exluded the lock guard
std::lock_guard<Lock> lock(_lockValue);as well and got the following output:Live Demo on coliru
The execution was trapped into an infinite recursion, and the online compiler aborted this after a certain time. (Sorry, coliru. I won't do it again.)