3

我在 Windows 7 上的 Qt 中需要一些帮助。似乎 QtreadyRead()信号是由异步过程调用发出的,这会导致代码在同一个线程中并发执行。

在我的示例中,我有一个队列,该队列应该由锁访问,DoRead()并且可以在其中访问。DoTimer()整个操作在 ui (main) thread中运行。但是有时会DoRead()发生所谓的死锁。代码在 中停止执行DoRead()。如果显示消息框并因此DoTimer()停止执行,则死​​锁是可重现的。但是我很惊讶地看到它OnRead()仍然被并发调用。对我来说唯一的解释是,这OnRead()是由 Windows APC 调用的。

请参阅 MSDN 文章异步过程调用

异步过程调用 (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);
}

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

4

1 回答 1

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

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

于 2012-01-28T01:00:32.463 回答