1

起動時にメインスレッドから2つのスレッドを起動するQtアプリケーションがあります。これらのスレッドは両方とも、QNetworkAccessManagerオブジェクトの個別のインスタンスを使用してネットワーク要求を行います。私のプログラムは約50%の確率でクラッシュし続けており、どのスレッドがクラッシュしているかわかりません。

2つのスレッド間で直接発生するデータ共有やシグナリングはありません。特定のイベントが発生すると、1つのスレッドがメインスレッドに信号を送り、メインスレッドが2番目のスレッドに信号を送ります。ただし、ログを印刷することで、シグナリング中にクラッシュが発生しないことを確信しています。

両方のスレッドの構造は次のとおりです。URLなどを除いて、スレッド間にほとんど違いはありません。

MyThread() : QThread() {
    moveToThread(this);
}

MyThread()::~MyThread() {
    delete m_manager;
    delete m_request;
}

MyThread::run() {
    m_manager = new QNetworkAccessManager();
    m_request = new QNetworkRequest(QUrl("..."));

    makeRequest();
    exec();
}

MyThread::makeRequest() {
    m_reply = m_manager->get(*m_request);
    connect(m_reply, SIGNAL(finished()), this, SLOT(processReply()));
    // my log line
}

MyThread::processReply() {
    if (!m_reply->error()) {
        QString data = QString(m_reply->readAll());
        emit signalToMainThread(data);
    }
    m_reply->deleteLater();
    exit(0);
}

奇妙なことに、スレッドの1つを開始しないと、プログラムは正常に実行されるか、少なくとも約20回の呼び出しでクラッシュしません。両方のスレッドが次々に実行される場合、プログラムはクラッシュしません。両方のスレッドを同時に起動して実行すると、プログラムは約半分の時間しかクラッシュしません。

ログから収集したもう1つの興味深い点は、プログラムがクラッシュするたびに、コメントでラベル付けされた行my log lineが両方のスレッドによって最後に実行されることです。そのため、どのスレッドがクラッシュを引き起こしているのかわかりません。しかし、QN​​etworkAccessManagerがどういうわけか責任があるのではないかと私は思います。

クラッシュの原因についてはかなり空白です。提案やアドバイスをいただければ幸いです。前もって感謝します。

4

1 に答える 1

0

まず第一に、あなたはそれを間違っています!最初にスレッドを修正します

// EDIT このパターンに関する私自身の経験から、多くの不明なクラッシュが発生する可能性があることがわかっています。いくつかのことを正し、問題の発見を明確にする可能性があるため、私はこのことを片付けることから始めます. また、どのように呼び出すのかわかりませんmakeRequest。QNetworkRequest についても。これは単なるデータ構造であるため、ヒープ上に作成する必要はありません。スタック構築で十分です。また、m_reply ポインターを上書きしないように覚えておく (または何らかの方法で保護する) 必要があります。何度も電話しますmakeRequestか?その場合、前のリクエストが終了した後に、現在処理されているリクエストが削除される可能性があります。

makeRequest を 2 回呼び出すとどうなりますか。

  1. makeRequest の最初の呼び出しで m_reply ポインターが割り当てられます。
  2. makeRequest の 2 回目の呼び出しで、2 回目に m_reply ポインターが割り当てられます (割り当てられたポインターは置き換えられますが、指定されたオブジェクトは削除されません)。
  3. 2 番目の要求は最初の要求より前に終了するため、processReply が呼び出されます。deleteLater は 2 番目にキューに入れられます
  4. イベントループのどこかで 2 番目の返信が削除されるため、m_reply ポインターはランダムな (削除された) メモリを指しています。
  5. 最初の応答が終了すると、別の processReply が呼び出されますが、それはガベージを指している m_reply で動作するため、m_reply での呼び出しごとにクラッシュが発生します。

考えられるシナリオの1つです。そのため、毎回クラッシュすることはありません。

返信の最後に exit(0) を呼び出す理由がわかりません。ここでも、makeRequest の呼び出しを複数回使用すると正しくありません。QThread は、スレッド プールではなく、単一のスレッドへのインターフェイスであることに注意してください。start()そのため、実行中のスレッド インスタンスで 2 回目の呼び出しを行うことはできません。また、エントリ ポイントにネットワーク アクセス マネージャを作成している場合は、run()後で同じ場所で削除する必要がありますexec()。これexec()はブロッキングであるため、スレッドが終了する前にオブジェクトが削除されないことに注意してください。

于 2012-06-07T13:19:37.270 に答える