我需要在Windows 7上的Qt中提供一些帮助。似乎Qt readyRead()
信号是由异步过程调用发出的,这导致代码在相同的线程中并发执行
在我的示例中,我有一个队列,应由DoRead()
访问,并在DoTimer()
中由锁访问。整个操作在ui(主)线程中运行 。但是有时候调用DoRead()
时会发生死锁。代码停止在DoRead()
中执行。如果显示消息框,则可以重现死锁,因此暂停执行DoTimer()
。但是我很惊讶地看到仍然同时调用OnRead()
。对我来说唯一的解释是,Windows {8}调用OnRead()
。
请参阅MSDN文章Asynchronus Procedure Calls:
异步过程调用(APC)是在特定线程的上下文中异步执行的函数。当APC排队到某个线程时,系统会发出软件中断。 下次安排线程时,它将运行APC功能。
我是否认为readyRead()
可能是APC?
在任何一种情况下,我该怎么做才能防止死锁?我需要访问DoRead()
中的队列来填充队列,并在DoTimer()
(当然还有其他方法)中读取,写入或删除相同队列中的条目。递归互斥锁不是解决方案,因为两个调用都发生在同一个线程中。
class QMySocket : public QTcpSocket {
public:
QMySocket() {
...
connect(this, SIGNAL(readyRead()), this, SLOT(DoRead()));
connect(_MyTimer, SIGNAL(timeout()), this, SLOT(DoTimer()));
...
}
private:
QTimer* _MyTimer;
QQueue<int> _MyQueue;
QMutex _Lock;
void DoRead() {
_Lock.lock(); // <-- Dead Lock here (same Thread ID as in DoTimer)
_MyQueue... // Do some queue operation
// DoSomething
_Lock.unlock();
}
void DoTimer() {
_Lock.lock();
QQueue<int>::iterator i = _MyQueue.begin();
while (i != _MyQueue.end()) { // Begin queue operation
if (Condition) {
QMessageBox::critical(...);
i = _MyQueue.erase(i);
} else {
i++;
}
} // end queue operation
_Lock.unlock();
}
};
编辑2 :这与我发现的APC无关。问题只是QMessageBox创建的额外消息循环。
而是直接调用QMessageBox,所有消息都将排队并在任何队列操作后显示。
void DoTimer() {
QList<QString> Messages;
QQueue<int>::iterator i = _MyQueue.begin();
while (i != _MyQueue.end()) { // Begin queue operation
if (Condition) {
Messages.append(...);
i = _MyQueue.erase(i);
} else {
i++;
}
} // end queue operation
QMessageBox::critical(Messages);
}
如果没有对队列的并发访问(不进行多线程处理),则不需要锁。
答案 0 :(得分:2)
您唯一的问题是致电
QMessageBox::critical(...);
此调用会阻止,直到您按下按钮。但是,既然你在保持锁定的同时调用了它,那么你的DoRead会死锁。
完全没有理由在按住锁定时打开消息框!
如果您仍希望DoTimer在显示消息框时做出响应,请不要使用QMessagebox :: critical等静态便捷方法。
最好这样做
// Somewhere in the constructor ...
QMessageBox* msgBox = new QMessageBox( this );
msgBox->setAttribute( QWidget::WA_DeleteOnClose );
msgBox->setStandardButtons( QMessageBox::Ok );
msgBox->setWindowTitle( tr("Error") );
msgBox->setModal( true );
//...
void DoTimer() {
_Lock.lock();
// DoSomething
_MyQueue... // Iterate over queue, and do some queue operation (delete entires for exmaple)
_Lock.unlock();
msgBox->setText( tr("DingDong!") );
if (!msgBox->isVisible())
msgBox->open( this, SLOT(msgBoxClosed(QAbstractButton*)) );
}
void MyWidget::msgBoxClosed(QAbstractButton*) {
qDebug("Byebye msgbox");
}
但是,仍然,从您的代码中我看不出任何理由使用互斥锁。没有并发性,对吧?