简体   繁体   English

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

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

I have the task of initialisation of an object, that is quite long. 我的任务是初始化一个对象,这很长。 What is the right way to go about it? 正确的做法是什么?

here is the code i have so far (for simplicity, initialisation consists of adding entries to string list only): 这是我到目前为止的代码(为简单起见,初始化仅包括将条目添加到字符串列表):

#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());
    }
}

The ides is to pass parameters to initialiser instance in constructor, check their validity and then run the initialisation itself. 想法是将参数传递给构造函数中的初始化程序实例,检查其有效性,然后运行初始化程序。 However, initialisation may take long and the application may be stopped abruptly; 但是,初始化可能会花费很长时间,并且应用程序可能会突然停止。 in this case initialiser should stop its activity properly and be cleaned up. 在这种情况下,初始化程序应正确停止其活动并进行清理。

I have read on several methods of running async tasks, but still haven't understood how to detect the stop signal. 我已经阅读了几种运行异步任务的方法,但是仍然不了解如何检测停止信号。 As far as i can see, running QRunnable in thread pool or using QtConcurrent::run() does not gives any mechanism of checking whether it is time to stop or not. 据我所知,在线程池中运行QRunnable或使用QtConcurrent :: run()并没有提供任何机制来检查是否应该停止。

Also, i am confused of the subject of how to pass the object being initialised properly to and from initialising task, so that it would be guaranteed to be cleaned up. 另外,我对如何正确地将正在初始化的对象传递给初始化任务和从初始化任务传递主题感到困惑,以便可以保证将其清理干净。 Same with initialiser; 与初始化程序相同; how can it be guaranteed to be cleaned up? 如何保证将其清理干净?

here is the code i currently use to launch initialisation: 这是我当前用于启动初始化的代码:

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

Well, the quick-and-dirty method would be to have a boolean variable (named pleaseStop or something) that the initializer thread checks every so often to see if the main thread has set it to true or not. 好吧,快捷方法是拥有一个布尔变量(命名为pleaseStop或其他名称),初始化线程会每隔一段时间检查一次布尔变量,以查看主线程是否将其设置为true。 If the main thread has set it to true, the thread task would see that and abort its initialization routine and exit. 如果主线程将其设置为true,则线程任务将看到该消息并中止其初始化例程并退出。

A slightly nicer method would be to break up the initialization task into smaller parts -- eg do 50 milliseconds worth of initialization, then if there is still more initialization left to do, call QTimer::singleShot(0, this, SLOT(DoSomeMore()) and return from the method, such that the DoSomeMore() slot gets called on the next iteration of the Qt event loop, but in the meantime any other pending signals (such as a PleaseQuitNow() type of signal coming in from the main thread, which could be connected to the QThread's quit() slot) would get processed. 稍微好一点的方法是将初始化任务分解为较小的部分-例如进行50毫秒的初始化,然后如果还有更多初始化要做,请调用QTimer :: singleShot(0,this,SLOT(DoSomeMore( )),然后从该方法返回,以便在Qt事件循环的下一次迭代中调用DoSomeMore()插槽,但与此同时,其他任何待处理的信号(例如从主设备进入的其他信号,如PleaseQuitNow()线程,可以连接到QThread的quit()插槽)将得到处理。

In fact, if you keep the time slices short enough, with the second method you may not even need to launch a second thread at all -- the initialization could run in the main thread but GUI events would still get serviced in a relatively timely manner, since they would get interspersed between short DoSomeMore() calls rather than being blocked by a single very lengthy DoEverythingAllInOneGo() call. 实际上,如果您将时间片保持足够短,那么使用第二种方法,您甚至可能根本不需要启动第二个线程-初始化可以在主线程中运行,但是GUI事件仍将以相对及时的方式得到服务。 ,因为它们会在短暂的DoSomeMore()调用之间散布,而不是被一个很长的DoEverythingAllInOneGo()调用所阻塞。

As for passing the data object back and forth, simply handing the initializer thread a pointer/reference to the data object is sufficient, as long as you are careful to ensure the following: 至于来回传递数据对象,只要小心确保以下各项,只需给初始化程序线程一个指向数据对象的指针/引用就足够了:

  1. The main thread shouldn't read or write the data object until the initializer thread has completed its work 在初始化程序线程完成其工作之前,主线程不应读取或写入数据对象
  2. The main thread must guarantee that the data object will remain valid until after the initializer thread has completed its work (eg don't delete the data object while the initializer thread might still be using it!) 主线程必须保证数据对象在初始化程序线程完成其工作之前将保持有效(例如,不要删除数据对象,而初始化程序线程可能仍在使用它!)

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

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