1

QEventLoop::exec を呼び出すと、特定の条件下でデッドロックするように見えるかなり単純なアプリケーションがあります。アプリケーションは、次の 2 つのシナリオでこの関数を呼び出します。

  • 特定のデータがソケットに到着したとき
  • タイマーイベント時

どちらの場合も、次のコンテキストで使用されます (単なる http クエリで、特別なことは何もありません)。

QNetworkReply::NetworkError HttpGetMessagesStrategy::syncHttp(const QUrl url, QByteArray &dst) const
{
    QNetworkRequest request(url);
    request.setRawHeader("Cache-Control", "no-cache");

    QNetworkAccessManager mgr;
    QEventLoop eventLoop;
    QObject::connect(&mgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
    QNetworkReply *reply = mgr.get(request);
    if (reply == NULL) {
        return QNetworkReply::UnknownNetworkError;
    }

    eventLoop.exec();

    QNetworkReply::NetworkError error = reply->error();
    if (error == QNetworkReply::NoError) {
        dst += reply->readAll();
    }
    delete reply;

    return error;
}

これを呼び出そうとすると、次のようになります。

...    
#56 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
#57 0x0000003404b804a2 in ?? () from /usr/lib64/libQtCore.so.4
#58 0x0000003404b7d928 in ?? () from /usr/lib64/libQtCore.so.4
#59 0x00000033fba38f0e in g_main_context_dispatch () from /lib64/libglib-2.0.so.0
#60 0x00000033fba3c938 in ?? () from /lib64/libglib-2.0.so.0
#61 0x00000033fba3ca3a in g_main_context_iteration () from /lib64/libglib-2.0.so.0
#62 0x0000003404b7d5f3 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#63 0x0000003404b56722 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#64 0x0000003404b569ec in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#65 0x00007f41c1b4eec7 in HttpGetMessagesStrategy::syncHttp (this=<value optimized out>, url=<value optimized out>, dst=...) at HttpGetMessagesStrategy.cpp:49
#70 0x000000000040e57c in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902bb5f0) at DevicePlugin.cpp:250
#71 0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#72 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

ご覧のとおり、HTTP 要求の応答を待つ代わりに、別のイベントを取得して処理を開始しました。その結果、アプリケーションが停止する前に現在のスレッドで膨大な数のフレーム (746) を取得し、次のような行が表示されます。

#0  0x00000033faa0efe0 in __pause_nocancel () from /lib64/libpthread.so.0
#1  0x00000033faa0917b in __pthread_mutex_lock_full () from /lib64/libpthread.so.0
#2  0x0000003404a702a3 in ?? () from /usr/lib64/libQtCore.so.4
#3  0x0000003404a6cd95 in QMutex::lock() () from /usr/lib64/libQtCore.so.4
#4  0x0000003404b57952 in QCoreApplication::postEvent(QObject*, QEvent*, int) () from /usr/lib64/libQtCore.so.4
#5  0x000000000040e293 in DevicePlugin::destroyConnection (this=0x1267390, c=0x1a85fb0) at DevicePlugin.cpp:194
#6  0x000000000040e5b6 in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902b9230) at DevicePlugin.cpp:254
#7  0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#8  0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

ここで何が間違っているのか、誰か親切に説明してもらえますか?

4

3 に答える 3

0

http://qt-project.org/doc/qt-4.8/qeventloop.html#exec

int QEventLoop::exec ( ProcessEventsFlags flags = AllEvents ) メイン イベント ループに入り、exit() が呼び出されるまで待機します。exit() に渡された値を返します。

exec() は QT イベント処理の表現であり、現在のスレッドが exit() で戻るまで常にブロッキング呼び出しです。これにより、メイン ループ exec() の外側のスレッドが競合することなく QT シグナルとスロットを使用できるようになります。

ヘッダー ファイルで定義された独自の処理スロットに finished() 呼び出しを接続する必要があります。

シグナルとスロットを使用し、QOBJECT マクロを使用する場合は、QObject (またはその他の QT オブジェクト) からすべてのクラスを継承することを忘れないでください。

于 2014-08-22T20:21:43.943 に答える
0

を使用しようとしてQNetworkManagerおりQNetworkReply、ブロックするように設計されていません。

代わりに使用することを検討するQTcpSocketか、非同期メカニズムを使用するようにプログラムを再設計してください。

于 2014-08-24T07:48:21.110 に答える