私のプログラムでは、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は出力しません。ここには競合状態があり、それを修正するには、イベントループがいつ開始するかを知る必要があります。
多分私は何かが欠けています、そして問題は何か違うものです...
実際に読んでくれてありがとう!ふぅ!