1

マルチスレッド 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 のスロット全体をラップしても何も得られません。

4

2 に答える 2

0

すべてのQThreadオブジェクトは GUI スレッドで作成され、それに属します。そのため、QThread 派生クラスで定義されたスロットは、オブジェクトが表すスレッドではなく、メインの GUI スレッドで実行されます。

このスロットを他のスレッドで実行したい場合、最も簡単な解決策は、WorkerThreadを使用してオブジェクトをこのスレッドに移動することmoveToThread(this)です。

ただし、これは最善の決定ではありません。あなたが投稿したコードを考えると、QThread をサブクラス化する必要がある理由はわかりません。デフォルトの QThread 実装は、すでにニーズを満たしています。WorkerThread クラスを削除し、別の QObject 派生クラス (WorkerObject など) を作成してから、それを を使用して新しいスレッドに移動する必要がありますQObject::moveToThread。その後、このオブジェクトのスロットは別のスレッドで実行されます。

于 2013-06-10T19:09:05.467 に答える