首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Qt -如何从线程创建QFuture

Qt -如何从线程创建QFuture
EN

Stack Overflow用户
提问于 2019-12-05 14:50:11
回答 2查看 1.3K关注 0票数 4

我目前正在编写一个编辑器程序;我需要编写一个特性,它要求使用项目的异步文件API在一行中加载多个文件,然后在加载这些文件后再执行一些计算。

在另一种语言中,这可能是通过异步/等待工作流来实现的,例如:

代码语言:javascript
复制
let firstFile = await FileAPI.loadFile("Foo.xxx", ...);
let otherFile = await FileAPI.loadFile("Bar/Foobar.xxx", ...);

与此代码等效的Qt将是使用QtConcurrent::run生成一个新线程,返回一个QFuture,并等待这个未来产生一个结果。

但是,在我工作的项目中,打开文件的API运行在一个工作线程上,这意味着我不能使用。这是,代码库的一个固定的,不可转让的部分.文件API的构造函数如下所示:

代码语言:javascript
复制
FileApiWorker* worker = new FileApiWorker();
m_workerThread = new QThread();
worker->moveToThread( m_workerThread );

// Input signals
connect( this, &FileApi::loadFile, worker, &FileApiWorker::loadFile);
connect( this, &FileApi::loadData, worker, &FileApiWorker::loadData);
connect( this, &FileApi::loadDir, worker, &FileApiWorker::loadDir);

这意味着我访问文件系统数据的唯一方法是调用一个发出信号的方法,该方法在另一个线程上启动计算,该线程最终在最后发出自己的信号来传递加载的数据。

对于上面的用例来说,这是非常不切实际的,因为与其说“做事情,加载数据,等待,继续做事情”,我实际上需要在另一个函数中说“做事情,加载数据(调用‘继续做事情’)和”继续做事情“,这在代码中引入了各种脆性。(而且,嗯,你知道,这正是我们发明未来的工作流程)

有什么方法可以从loadFile 方法中创建QFuture,或者将来的等效对象(可以在方法中等待),因为 loadFile 总是在同一个工作线程上运行,并且不允许创建新的线程?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-02-04 10:13:37

在Qt中创建QFuture的最简单方法是使用无文档的QFutureInterface类。

示例代码:

代码语言:javascript
复制
Q_DECLARE_METATYPE( QFutureInterface<FileData> );

// ...

qRegisterMetaType<QFutureInterface<FileData>>();

FileApiWorker* worker = new FileApiWorker();
connect( this, &FileApi::loadFile_signal, worker, &FileApiWorker::loadFile_signal);

// ...

QFuture<FileData> FileApi::loadFile()
{
    QFutureInterface<FileData> futureInterface;
    // IMPORTANT: This line is necessary to be able to wait for the future.
    futureInterface.reportStarted();

    emit loadFile_signal(futureInterface);
    return futureInterface.future();
}

FileApiWorker::loadFile_signal(QFutureInterface<FileData>& futureInterface)
{
    // Do some things
    // ...
    futureInterface.reportResult(...);
    // IMPORTANT: Without this line, future.waitForFinished() never returns.
    futureInterface.reportFinished();
}

应考虑的一些因素:

  • 上面的代码使用Q_DECLARE_METATYPE;为了能够通过一个跨线程信号传递QFutureInterface,这是必要的。准确地说,如果不包括connect行,Q_DECLARE_METATYPE行将无法编译;如果不调用qRegisterMetaType,则emit loadFile_signal行将在运行时失败。详情请参见关于类型的Qt文档
  • 您可以传播错误,通过这种方式调用loadFile().waitForFinished()会抛出错误。为此,您需要创建一个继承QException的特殊用途类,然后调用: futureInterface.reportException( MyException(.) );futureInterface.reportFinished(); 在你的错误路径。 QException本质上是需要在线程之间传输的实际异常的包装器。详情请参见文献资料
  • 虽然QFutureInterface是稳定的,并且大部分具有与QFuture和QFutureWatcher相同的API,但它仍然是一个没有文档的特性,这可能会让贡献者意外地在共享代码库中看到它。这个类可能违反直觉,如果你不尊重上面的要点(我不得不通过尝试和错误来学习),就会默默地失败。必须在使用QFutureInterface的任何共享代码的注释中强调这一点。类的源代码可以在这里找到
票数 4
EN

Stack Overflow用户

发布于 2019-12-09 00:57:29

海事组织,不使用现成的解决方案(AsyncFuture)并试图从头开始重写是很奇怪的。

但我可以建议我自己的“轮子”:兰博达作为一个插槽。

代码语言:javascript
复制
void FileApi::awaitLoadFile()
{
    qDebug() << "\"await\" thread is" << thread(); 

    emit loadFile("Foo.xxx");

    static bool once = connect(m_worker, &FileApiWorker::loadFileDone, this, // there is possible to avoid the third "this" parameter, but it is important to specify the lifetime of the connection and the receiver context while using lambdas
        [=](QByteArray result)
    {
        qDebug() << "\"comeback-in-place\" thread is" << thread(); // will be the same as "await" thread was

        // do what you need with your result
    },
    Qt::QueuedConnection // do not forget
    );

    qDebug() << "here is an immediate return from the \"await\" slot";
}

实用弧片新信号时隙语法- Qt Wiki

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59197694

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档