3

Windows 7 の Qt で支援が必要です。readyRead()非同期プロシージャ呼び出しによって Qt シグナルが発行され、コードが同時に実行されますが、同じスレッドで実行されるようです。

私の例では、ロックによってアクセスされ、DoRead()アクセスされるキューがあります。DoTimer()操作全体が ui (メイン) スレッドで実行されています。しかし、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 に答える