私の Qt アプリケーションQThread
では、重い計算タスクを定期的に実行する必要がある を作成します。メインQApplication
スレッドは、GUI (例には含まれていません) を維持し、定期的な更新も実行することになっています。両方のスレッドには、通常の update() 呼び出しを有効にする独自のタイマーがあります。
問題:ワーカー スレッドの計算ワークロードが重大な値を超えると、メイン スレッドがタイマー イベントの受信を停止します。
コード例を以下に示します。メインスレッドの場合は update() が呼び出されたときに「Main」、ワーカースレッドの場合は「Worker」を出力します。実行すると、「Worker」が定期的に出力され、「Main」が正確に 2 回 (最初に 1 回、約 5 秒に 1 回) 表示されることがわかります。フル機能の GUI アプリケーションの場合、これは事実上、GUI が完全にフリーズすることを意味します。
いくつかの観察。
- 内部サイクルに (1000 ではなく) 100 制限を設定してワークロードを削減すると、問題が解決します (両方の update() メソッドが定期的に呼び出されます)。
- ワーカー スレッド タイマー シグナルの接続タイプを Qt::DirectConnection に設定すると、問題が解決します。
ご覧のとおり、これにはいくつかの回避策がありますが、元のコードの問題を説明してくれる人がいれば幸いです。スレッドがイベント ループを個別に実行することを期待しています。長い update() 操作によってワーカー スレッド イベント ループをブロックしていることはわかっていますが、一体なぜそれがメイン スレッドに影響を与えるのでしょうか?
QConcurrent
PS はい、私は代替案について知っています。しかし、私は理解したいと思います。
test.cpp
#include <windows.h>
#include <QApplication>
#include "test.h"
HANDLE mainThread_ = INVALID_HANDLE_VALUE;
QApplication *app_ = 0;
MyObj *obj_ = 0;
MyThread *thread_ = 0;
MyObj::MyObj()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}
void MyObj::update()
{
printf("Main\n");
}
void MyThread::run()
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
exec();
}
void MyThread::update()
{
printf("Worker\n");
// do some hard work
float f = 0.f;
for (int i=0; i < 100000; ++i)
{
for (int j=0; j < 1000; ++j)
{
f += i * j;
}
}
}
int main()
{
int argc = 0;
app_ = new QApplication(argc, 0);
obj_ = new MyObj();
thread_ = new MyThread();
thread_->start();
QApplication::exec();
return 0;
}
test.h
#include <QTimer>
#include <QThread>
class MyObj : public QObject
{
Q_OBJECT
public:
MyObj();
public slots:
void update();
private:
QTimer *timer_;
};
class MyThread : public QThread
{
Q_OBJECT
public:
void run();
public slots:
void update();
private:
QTimer *timer_;
};
UPD:立派なメンバーからいくつかの回答を得ました (以下をお読みください)。ここで、特にどのような誤った考えが私のコードを壊したのかを明確にしたいと思います。
ご覧のとおり、2 つのスレッドがそれぞれ何らかの update() プロシージャを定期的に実行する計画でした。私の間違いは、 update() を単なる手順として考えていたことであり、それはslotです。独自のスレッド アフィニティを持つ特定のオブジェクトのスロット。つまり、その本体はそのスレッドで実行されます (信号が Qt::DirectConnection でディスパッチされない限り)。今、私はタイマーでそれをすべてうまくやったようです-それらのそれぞれは異なるスレッドに属しています-しかしupdate()で物事を台無しにしました。そのため、メインスレッドで両方の update() プロシージャを実行することになりました。明らかに、ある時点でイベント ループがタイマー イベントであふれ、決して反復が終了しません。
解決策については。「あなたのやり方は間違っている」を読んだことがあるなら (実際そうすべきです)、すべてのロジックを QThread からサブクラス化されていないが、個別に作成されて moveToThread( )。個人的には、オブジェクトがスレッドを制御するだけでスレッドに属していないことを覚えていれば、QThread からサブクラス化しても問題はないと思います。したがって、そのスレッドで実行したいコードの場所ではありません。