我目前正在编写一个编辑器程序;我需要编写一个特性,它要求使用项目的异步文件API在一行中加载多个文件,然后在加载这些文件后再执行一些计算。
在另一种语言中,这可能是通过异步/等待工作流来实现的,例如:
let firstFile = await FileAPI.loadFile("Foo.xxx", ...);
let otherFile = await FileAPI.loadFile("Bar/Foobar.xxx", ...);与此代码等效的Qt将是使用QtConcurrent::run生成一个新线程,返回一个QFuture,并等待这个未来产生一个结果。
但是,在我工作的项目中,打开文件的API运行在一个工作线程上,这意味着我不能使用。这是,代码库的一个固定的,不可转让的部分.文件API的构造函数如下所示:
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 总是在同一个工作线程上运行,并且不允许创建新的线程?。
发布于 2020-02-04 10:13:37
在Qt中创建QFuture的最简单方法是使用无文档的QFutureInterface类。
示例代码:
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本质上是需要在线程之间传输的实际异常的包装器。详情请参见文献资料。发布于 2019-12-09 00:57:29
海事组织,不使用现成的解决方案(AsyncFuture)并试图从头开始重写是很奇怪的。
但我可以建议我自己的“轮子”:兰博达作为一个插槽。
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
https://stackoverflow.com/questions/59197694
复制相似问题