ヤンの発言は不正確です。moveToThreadを使用することは、適切な動作を実現する1つの方法ですが、唯一の方法ではありません。
別の方法は、runメソッドをオーバーライドして、そこのスレッドが所有するオブジェクトを作成することです。次に、exec()を呼び出します。QThreadはシグナルを持つことができますが、接続がすべてキューに入れられていることを確認してください。また、Threadオブジェクトへのすべての呼び出しは、キュー接続を介して接続されているスロットを介して行う必要があります。あるいは、関数呼び出し(実行の呼び出し元スレッドで実行される)は、スレッド(runメソッドで作成される)が所有するオブジェクトへのシグナルをトリガーできます。この場合も、接続をキューに入れる必要があります。
ここで注意すべきことの1つは、コンストラクタとデストラクタが実行のメインスレッドで実行されていることです。建設とクリーンアップは実行中に実行する必要があります。runメソッドがどのようになるかの例を次に示します。
void MythreadDerrivedClass::run()
{
constructObjectsOnThread();
exec();
destructObjectsOnThread();
m_waitForStopped.wakeAll();
}
ここで、constructObjectsOnThreadには、コンストラクターに属していると思われるコードが含まれます。オブジェクトはdestructObjectsOnThreadで割り当て解除されます。実際のクラスコンストラクターはexit()メソッドを呼び出し、exec()を終了させます。通常、実行が戻るまで、待機条件を使用してデストラクタに留まります。
MythreadDerivedClass::~MythreadDerivedClass()
{
QMutexLocker locker(&m_stopMutex);
exit();
m_waitForStopped.wait(locker.mutex(), 1000);
}
繰り返しになりますが、コンストラクタとデストラクタは親スレッドで実行されています。スレッドが所有するオブジェクトは、run()メソッドで作成し、実行を終了する前に破棄する必要があります。クラスデストラクタは、スレッドに終了するように指示し、QWaitConditionを使用して、スレッドが実際に実行を終了するのを待つ必要があります。このようにすると、QThread派生クラスのヘッダーにQ_OBJECTマクロが含まれ、シグナルとスロットが含まれることに注意してください。
KDEライブラリを活用できる場合の別のオプションは、KDEのThreadWeaverです。これは、スレッドプールを活用するという点で、QtConcurrentRunに似たより完全なタスクベースのマルチタスク実装です。Qtのバックグラウンドを持っている人なら誰でも知っているはずです。
そうは言っても、あなたが同じことをするc ++ 11の方法にオープンであるなら、私はを見るでしょうstd::async
。一つには、Qtに依存しなくなりますが、APIは何が起こっているのかをより明確にします。MythreadDerivedClassクラスがQThreadから継承されると、読者はMythreadDerivedClassがスレッドであり(継承関係があるため)、そのすべての関数がスレッド上で実行されているという印象を受けます。ただし、run()
実際にスレッドで実行されるのはメソッドのみです。std :: asyncは正しく使用するのが簡単で、落とし穴が少なくなっています。私たちのコードはすべて、最終的には他の誰かによって維持され、これらのようなものは長期的には重要です。
C ++ 11 / w QTの例:
class MyThreadManager {
Q_OBJECT
public:
void sndProgress(int percent)
void startThread();
void stopThread();
void cancel() { m_cancelled = true; }
private:
void workToDo();
std::atomic<bool> m_cancelled;
future<void> m_threadFuture;
};
MyThreadedManger::startThread() {
m_cancelled = false;
std::async(std::launch::async, std::bind(&MyThreadedManger::workToDo, this));
}
MyThreadedManger::stopThread() {
m_cancelled = true;
m_threadfuture.wait_for(std::chrono::seconds(3))); // Wait for 3s
}
MyThreadedManger::workToDo() {
while(!m_cancelled) {
... // doWork
QMetaInvoke::invokeMethod(this, SIGNAL(sndProgress(int)),
Qt::QueuedConnection, percentDone); // send progress
}
}
基本的に、ここで得られるものは、QThreadを使用したコードの外観とworkToDo()
それほど変わりませんが、スレッド上でのみ実行され、MyThreadManagerがスレッド自体ではなくスレッドのみを管理していることがより明確になります。また、 MetaInvokeを使用して、進行状況の更新を送信するためのキューに入れられた信号を送信し、進行状況のレポート要件を処理しています。MetaInvokeの使用はより明確であり、常に正しいことを行います(スレッドマネージャーからの他のクラスのスロットにシグナルを接続する方法は関係ありません)。スレッドのループがアトミック変数をチェックして、プロセスがいつキャンセルされるかを確認し、キャンセル要件を処理していることがわかります。