マルチスレッド Qt アプリケーションを変更していて、予期しない動作に直面しました。
QThread から継承されたクラス WorkerThread があり、メソッドrun()
がいくつかの作業を行っています。handleSuccess()
WorkerThread のオブジェクトのスロットは、別のメイン スレッドからのシグナルに接続されます。このシグナルは、サーバーからの非同期の着信接続に応答して送信されます。私が理解しているようにrun()
、フロー制御が実行されている間はexec()
イベントループに含まれていないため、スロットを呼び出すことはできません。代わりに、アプリのログで、スレッドが中間点で作業を行っていることを確認しrun()
、準備をせずにすぐにスロットに入ります。
class WorkerThread : public QThread {
Q_OBJECT
public:
WorkerThread() { moveToThread(this); /* further initialization */ }
void run();
void foo();
public slots:
void handleSuccess(const QByteArray &);
void shutdown();
private:
QMutex mutex;
MyResource resource;
volatile bool mShutdown;
/* ... other declarations */
};
void WorkerThread::foo() {
QMutexLocker locker(&mutex);
/* Working with the guarded resource */
}
void WorkerThread::run() {
QEventLoop signalWaiterEventLoop;
connect(this, SIGNAL(terminated()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
connect(this, SIGNAL(shutdownThread()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
connect(this, SIGNAL(dbChanged()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
while (!mShutdown) {
signalWaiterEventLoop.exec();
if (mShutdown) break;
/* ... useful payload */
foo();
/* ... another payload */
}
}
void WorkerThread::handleSuccess(const QByteArray & data) {
log(Q_FUNC_INFO, __LINE__, __FILE__);
QMutexLocker locker(&mutex);
/* processing data */
}
void WorkerThread::shutdown()
{
mShutdown = true;
emit shutdownThread();
}
オブジェクトはメイン スレッドで作成および初期化されます。
void SomeClass::init() {
/* ... */
mWorker = new WorkerThread();
connect(connect(mProtocol, SIGNAL(dataReceived(QByteArray)), mWorker, SLOT(processSuccess(QByteArray)),
Qt::QueuedConnection));
mWorker->start();
/* ... */
}
エラーが発生するのは、foo()
gets が inside から呼び出されてミューテックスをロックし、フロー制御が何らかの理由run()
で渡され、最後にミューテックスをロックしようとしてデッドロックが発生した場合です。これはすべて単一のスレッドで発生することを強調します。ログに記録しました。実際、エラーは安定しており、毎回同じ場所で発生します。私が何かを考慮していないことは明らかですが、正確には何ですか?handleSuccess()
handleSuccess()
更新WorkerThread
を
書き直し、Worker: public QObject
前者run()
は public slothandleNewArrivals()
になりました。これは、シグナルに応答してスレッドのイベントループで呼び出されます。それでも、問題は残ります: の途中で別のスロットが実行されhandleNewArrivals()
ます。中断の場所は安定しており、ここにあります (seek evaluateTo()
):
bool hasContent = false;
QByteArray outData;
QBuffer inputBuffer(&data), outputBuffer(&outData);
inputBuffer.open(QIODevice::ReadOnly);
outputBuffer.open(QIODevice::WriteOnly|QIODevice::Append);
QXmlQuery xmlQuery;
xmlQuery.bindVariable("inputDocument", &inputBuffer);
xmlQuery.setQuery("doc($inputDocument)/commands//*");
QXmlSerializer serializer(xmlQuery, &outputBuffer);
log(Q_FUNC_INFO, __LINE__, __FILE__);
// the log line above appears, then after 1 ms the line
// from handleSuccess() appears successively.
xmlQuery.evaluateTo(&serializer);
// this line is never shown
log(Q_FUNC_INFO, __LINE__, __FILE__);
QXmlStreamReader xmlReader(outData);
while (!xmlReader.atEnd()) {
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::Invalid) {
ufo::logT(this) << tr("Invalid token: %1").arg(xmlReader.errorString());
continue;
}
if (!xmlReader.isStartDocument() && !xmlReader.isEndDocument()) {
xmlWriterDb.writeCurrentToken(xmlReader);
hasContent = true;
}
}
ufo::logT(this) << tr("Selected data from payment, hasContent = %1").arg(hasContent?"true":"false");
inputBuffer.close();
outputBuffer.close();
QXmlQuery::evaluateTo と QXmlSerializer で何がこのようなジャンプを引き起こす可能性がありますか? 例外または unix のような信号 (ソフトウェアは Windows、mingw32 で実行されます) または何かがあるようですがevaluateto()
、try-catch のスロット全体をラップしても何も得られません。