2

私のプログラムでは、QThreadをサブクラス化しており、次のrun()ように仮想メソッドを実装しました。

void ManagerThread::run() {

    // do a bunch of stuff,
    // create some objects that should be handled by this thread
    // connect a few signals/slots on the objects using QueuedConnection

    this->exec(); // start event loop
}

ここで、別のスレッド(これをMainThread)で開始し、そのシグナルManagerThread待ちstarted()ます。その後、で処理する必要のあるシグナルとスロットの使用に進みますManagerThread。ただし、started()シグナルは基本的に呼び出される直前に送信run()されるため、スレッドスケジューリングによってはMainThread、イベントループがまだ開始されていないため、からのシグナルが失われます。

編集:それは問題ではないことがわかりました、それは信号が時間内に接続されていないだけですが、同じ理由で)

電話をかける直前に信号を発することができましたexec()が、それも問題を引き起こしています。

イベントループが開始されたことを知るための明確で簡単な方法はありますか?

ありがとう!

EDIT2 :(解決策)

了解しました。問題は私が言ったとおりではないことがわかりました。イベントループが開始されていないという事実は問題ではありません。シグナルは開始されるまでキューに入れられるはずだからです。started()問題は、呼び出される前に信号が発信されるため、一部の信号が呼び出されるのに間に合わないことrun()です。

解決策、すべての接続の後、実行の直前に別のカスタム信号を発行することです。これにより、すべての信号/スロットが確実に接続されます。

これは私の問題の解決策ですが、実際にはスレッドのタイトルに対する答えではありません。タイトルに答える答えを受け入れました。

instance()好奇心旺盛な人のために、以下のすべてのコードを残しました。解決策は、メソッド内の別のシグナルを待つことです。


コード:

多くの人が私はシグナルを失うことはできないと言っているので、これが私のクラス全体の実装です。必要なものだけに簡略化します。

これがへのインターフェースManagerThreadです:

// singleton class
class ManagerThread: public QThread {

    Q_OBJECT

    // trivial private constructor/destructor

    public:
    static ManagerThread* instance();

    // called from another thread
    public:
    void doSomething(QString const& text);

    // emitted by doSomething,
    // connected to JobHandler whose affinity is this thread.
    signals:
    void requestSomething(QString const& text);

    // reimplemented virtual functions of QThread
    public:
    void run();

    private:
    static QMutex s_creationMutex;
    static ManagerThread* s_instance;
    JobHandler* m_handler; // actually handles the requests
};

いくつかの関連する実装。スレッドのシングルトンインスタンスの作成:

ManagerThread* ManagerThread::instance() {
    QMutexLocker locker(&s_creationMutex);
    if (!s_instance) {
        // start socket manager thread, and wait for it to finish starting
        s_instance = new ManagerThread();

        // SignalWaiter essentially does what is outlined here:
        // http://stackoverflow.com/questions/3052192/waiting-for-a-signal
        SignalWaiter waiter(s_instance, SIGNAL(started()));
        s_instance->start(QThread::LowPriority);
        qDebug() << "Waiting for ManagerThread to start";
        waiter.wait();
        qDebug() << "Finished waiting for ManagerThread thread to start.";
    }

    return s_instance;
}

シグナル/スロットを設定し、イベントループを開始する実行の再実装:

void ManagerThread::run() {
   // we are now in the ManagerThread thread, so create the handler
   m_handler = new JobHandler();

   // connect signals/slots
   QObject::connect(this,
                    SIGNAL(requestSomething(QString const&)),
                    m_handler,
                    SLOT(handleSomething(QString const&)),
                    Qt::QueuedConnection);

   qDebug() << "Starting Event Loop in ManagerThread";

   // SOLUTION: Emit signal here and wait for this one instead of started()

   this->exec(); // start event loop
}

処理を正しいスレッドに委任する関数。これは私が失われた信号を発するところです:

void ManagerThread::doSomething(QString const& text) {

    qDebug() << "ManagerThread attempting to do something";

    // if calling from another thread, have to emit signal
    if (QThread::currentThread() != this) {
        // I put this sleep here to demonstrate the problem
        // If it is removed there is a large chance the event loop
        // will not start up in time to handle the subsequent signal
        QThread::msleep(2000);  
        emit(requestSomething(text));
    } else {
       // just call directly if we are already in the correct thread
       m_handler->handleSomething(text);
    }
}

MainThread最後に、イベントループが時間内に開始されない場合に失敗するコードは次のとおりです。

ManagerThread::instance()->doSomething("BLAM!");

ハンドラーがテキストを出力するだけだとすると、実行が成功すると次のように出力されます。

ManagerThreadが開始するの
を待っていますManagerThreadスレッドが開始するのを待っています。
ManagerThreadでイベントループを開始します
ManagerThreadが何か
BLAMを実行しようとしています!

そして、これが失敗した実行で何が起こるかです:

ManagerThreadが開始するの
を待っていますManagerThreadスレッドが開始するのを待っています。
ManagerThreadが何かをしようとしている
ManagerThreadでイベントループを開始する

明らかに、イベントループは信号が発信された後に開始され、BLAMは出力しません。ここには競合状態があり、それを修正するには、イベントループがいつ開始するかを知る必要があります。

多分私は何かが欠けています、そして問題は何か違うものです...

実際に読んでくれてありがとう!ふぅ!

4

4 に答える 4

3

接続を正しく設定すれば、信号を失うことはありません。ただし、スレッドのイベントループの開始に関する通知を本当に受け取りたい場合は、を呼び出す前に右で試すことができQTimer::singleShot()ます。イベントループの開始時に配信され、1回だけ配信されます。run()exec()

于 2011-03-10T16:06:15.280 に答える
1

QSemaphoreスレッド間のシグナルを確認できます。スロットとシグナルは、同じスレッドでのUIイベントとコールバックに適しています。

編集:あるいは、セマフォが適用できない場合とQMutex組み合わせることができます。QWaitConditionManagerThreadをMainThreadと組み合わせてどのように使用しているかを確認するためのより多くのサンプルコードが役立ちます。

于 2011-03-10T15:52:25.007 に答える
1

これは問題ではありません。connect()スレッド間のシグナルはキューに入れられます(より具体的には、スレッド間の直接接続は安全ではないため、呼び出しでキューに入れられるように設定する必要があります)。

http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads

于 2011-03-10T16:01:46.397 に答える
0

ManagerThreadのコンストラクターでシグナル/スロット接続を作成できます。このようにして、run()が呼び出される前でも確実に接続されます。

于 2011-03-10T17:27:49.753 に答える