繁体   English   中英

如何在Qt中正确进行异步初始化?

[英]How to make asynchronous initializing correctly in Qt?

我的任务是初始化一个对象,这很长。 正确的做法是什么?

这是我到目前为止的代码(为简单起见,初始化仅包括将条目添加到字符串列表):

#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;

好吧,快捷方法是拥有一个布尔变量(命名为pleaseStop或其他名称),初始化线程会每隔一段时间检查一次布尔变量,以查看主线程是否将其设置为true。 如果主线程将其设置为true,则线程任务将看到该消息并中止其初始化例程并退出。

稍微好一点的方法是将初始化任务分解为较小的部分-例如进行50毫秒的初始化,然后如果还有更多初始化要做,请调用QTimer :: singleShot(0,this,SLOT(DoSomeMore( )),然后从该方法返回,以便在Qt事件循环的下一次迭代中调用DoSomeMore()插槽,但与此同时,其他任何待处理的信号(例如从主设备进入的其他信号,如PleaseQuitNow()线程,可以连接到QThread的quit()插槽)将得到处理。

实际上,如果您将时间片保持足够短,那么使用第二种方法,您甚至可能根本不需要启动第二个线程-初始化可以在主线程中运行,但是GUI事件仍将以相对及时的方式得到服务。 ,因为它们会在短暂的DoSomeMore()调用之间散布,而不是被一个很长的DoEverythingAllInOneGo()调用所阻塞。

至于来回传递数据对象,只要小心确保以下各项,只需给初始化程序线程一个指向数据对象的指针/引用就足够了:

  1. 在初始化程序线程完成其工作之前,主线程不应读取或写入数据对象
  2. 主线程必须保证数据对象在初始化程序线程完成其工作之前将保持有效(例如,不要删除数据对象,而初始化程序线程可能仍在使用它!)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM