Qt QTcpSocket:如何防止readyRead信号死锁?

时间:2012-01-27 17:29:01

标签: qt sockets deadlock message-loop

我需要在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);
}

如果没有对队列的并发访问(不进行多线程处理),则不需要锁。

1 个答案:

答案 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");
}

但是,仍然,从您的代码中我看不出任何理由使用互斥锁。没有并发性,对吧?