0

オブジェクトを初期化するタスクがありますが、これはかなり長いです。それについて行く正しい方法は何ですか?

ここに私がこれまでに持っているコードがあります(簡単にするために、初期化は文字列リストへのエントリの追加のみで構成されています):

#ifndef TASKINITIALIZER_H
#define TASKINITIALIZER_H
#include <QDir>
#include <QThread>
#include <QObject>
#include "task.h"
class TaskInitializer:public QThread
{
    Q_OBJECT
    QDir dir;
    QString msg;
    Task &result;
public:
    TaskInitializer(QString dname, bool png, bool jpg, bool bmp, Task &res);
    ~TaskInitializer();
    const QString& getMessage();
    bool isOk();
private:
    void run();

};

#endif // TASKINITIALIZER_H


#include <QDir>
#include <QDirIterator>
#include "taskinitializer.h"

TaskInitializer::TaskInitializer(QString dname, bool png, bool jpg, bool bmp, Task & res):
    dir(dname),result(res)
{
    QStringList filters;
    if (png)
        filters << "*.png";
    if(jpg)
    {
        filters << "*.jpeg" << "*.jpg";
    }
    if(bmp)
        filters << "*.bmp";
    dir.setNameFilters(filters);
}

TaskInitializer::~TaskInitializer()
{
}

const QString &TaskInitializer::getMessage()
{
    return msg;
}


bool TaskInitializer::isOk()
{
    if (!dir.exists())
    {
        msg = ("Directory does not exist");
        return false;
    }
    if (dir.nameFilters().length() < 1)
    {
        msg = ("No image types chosen");
        return false;
    }
    return true;
}

void TaskInitializer::run()
{
    QDirIterator di(dir,QDirIterator::Subdirectories);
    while(di.hasNext())
    {
        result.addFilename(di.next());
    }
}

アイデアは、コンストラクターの初期化インスタンスにパラメーターを渡し、それらの有効性を確認してから、初期化自体を実行することです。ただし、初期化に時間がかかり、アプリケーションが突然停止する場合があります。この場合、イニシャライザはそのアクティビティを適切に停止し、クリーンアップする必要があります。

非同期タスクを実行するいくつかの方法を読みましたが、停止信号を検出する方法をまだ理解していません。私が見る限り、スレッドプールで QRunnable を実行したり、 QtConcurrent::run() を使用したりしても、停止する時間かどうかを確認するメカニズムはありません。

また、クリーンアップが保証されるように、適切に初期化されているオブジェクトを初期化タスクとの間で渡す方法について混乱しています。イニシャライザと同じです。クリーンアップされることをどのように保証できますか?

初期化を開始するために現在使用しているコードは次のとおりです。

_temp = new Task();
TaskInitializer *worker = new TaskInitializer(_directoryName,flags[2],flags[1],flags[0],*_temp);
if (!worker->isOk())
{
    delete _temp;
    _temp = NULL;
    emit logMessage(worker->getMessage());
    return _temp;
}

//clearTempTask();
emit logMessage("New task created");
connect(worker,SIGNAL(finished()),SIGNAL(taskInitialized()));
connect(worker,SIGNAL(finished()),worker,SLOT(deleteLater()));
worker->start();
worker = NULL;
4

1 に答える 1

0

簡単な方法は、初期化スレッドが頻繁にチェックしてメインスレッドが true に設定されているかどうかを確認するブール変数 (pleaseStop などの名前) を用意することです。メイン スレッドが true に設定した場合、スレッド タスクはそれを確認し、初期化ルーチンを中止して終了します。

少し良い方法は、初期化タスクを小さな部分に分割することです。たとえば、50 ミリ秒相当の初期化を行い、さらに初期化が残っている場合は、QTimer::singleShot(0, this, SLOT(DoSomeMore( )) とメソッドから戻り、 DoSomeMore() スロットが Qt イベント ループの次の反復で呼び出されますが、その間に他の保留中のシグナル (メインから着信する PleaseQuitNow() タイプのシグナルなど) QThread の quit() スロットに接続できるスレッド) が処理されます。

実際、タイム スライスを十分に短くしておくと、2 番目の方法で 2 番目のスレッドを起動する必要さえまったくない場合があります。初期化はメイン スレッドで実行できますが、GUI イベントは比較的タイムリーに処理されます。これは、単一の非常に長い DoEverythingAllInOneGo() 呼び出しによってブロックされるのではなく、短い DoSomeMore() 呼び出しの間に散在するためです。

データ オブジェクトをやり取りする場合は、次のことを注意して行う限り、イニシャライザ スレッドにデータ オブジェクトへのポインタ/参照を渡すだけで十分です。

  1. メイン スレッドは、イニシャライザ スレッドが作業を完了するまで、データ オブジェクトの読み取りまたは書き込みを行うべきではありません。
  2. メイン スレッドは、イニシャライザ スレッドがその作業を完了するまで、データ オブジェクトが有効なままであることを保証する必要があります (たとえば、イニシャライザ スレッドがまだデータ オブジェクトを使用している可能性がある間は、データ オブジェクトを削除しないでください!)。
于 2013-06-07T06:41:53.853 に答える