C++ 如何使异步API与需要同步性的API保持一致?

C++ 如何使异步API与需要同步性的API保持一致?,c++,qt,asynchronous,synchronous,C++,Qt,Asynchronous,Synchronous,这个问题源于我之前问过的一个问题 简而言之,image provider抽象类需要实现一个方法,该方法通过参数“url”请求图像并返回图像。例如,单个方法请求并返回图像。但是Qt的QNetworkAccessManager类严格设计为异步使用,例如,您使用一种方法请求url,并通过连接到完成请求后发出的信号来拦截url。例如,它必须分为两个步骤进行分解,这就提出了一个问题:如何使用一个要分解的类来实现映像提供程序所需的单个方法(考虑到我用来强制同步的黑客导致了混乱)?一般的答案是:无法安全地完成

这个问题源于我之前问过的一个问题


简而言之,image provider抽象类需要实现一个方法,该方法通过参数“url”请求图像并返回图像。例如,单个方法请求并返回图像。但是Qt的
QNetworkAccessManager
类严格设计为异步使用,例如,您使用一种方法请求url,并通过连接到完成请求后发出的信号来拦截url。例如,它必须分为两个步骤进行分解,这就提出了一个问题:如何使用一个要分解的类来实现映像提供程序所需的单个方法(考虑到我用来强制同步的黑客导致了混乱)?

一般的答案是:无法安全地完成。虽然在特定的情况下,这是可以做到的,但它需要对所涉及的代码进行仔细的检查,并提供一些证据证明由于可重入性没有问题

至于同步类的使用,您只需要在
requestXxxx
方法的实现中运行一个本地事件循环。如果它在专用线程中运行,那么可重入性问题就不是什么问题,因为您可以控制线程中活动的对象

由于
QQuickImageProvider
的实现可以指定
ForceAsynchronousImageLoading
标志,因此您的提供程序将在自己的线程中运行,并且可以安全地运行自己的消息循环

请注意,默认的QML映像提供程序将URI作为输入,并且非常乐意从web加载映像—因此在这种情况下您不必担心它

因此,即使您的自定义图像提供程序是完全不必要的,如果您要创建它,以下是您可能的做法:

class MyImageProvider : public QQuickImageProvider {
public:
  MyImageProvider();
  Flags flags() const { return ForceAsynchronousImageLoading; }
  QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize)
    Q_DECL_OVERRIDE;
}

QImage MyImageProvider::requestImage(
  const QString & id, QSize * size, const QSize & requestedSize)
{
  QImage image;
  QEventLoop loop;
  QNetworkAccessManager mgr;
  QObject::connect(&mgr, &QNetworkAccessManager::finished, 
    [&loop, size](QNetworkReply* reply) {
       image.load(reply, "JPG");
       if (size) *size = image.size();
       loop.quit();
       delete reply;
    });
  mgr.get(QNetworkRequest(QUrl(id)));
  loop.exec();
  return image;
}

您的意思是,在这种特殊情况下,
QEventLoop
默认情况下没有实例化自己的线程,而是在主线程中运行?@user3735658 a
QEventLoop
没有实例化任何线程!!但是,由于您需要一个事件循环来运行异步网络访问管理器,并且您需要在执行
requestXxxx
时运行该事件循环,因此您需要创建一个事件循环并
exec()
它。因此,我还必须实例化一个线程,将对象移动到该线程,但仍然锁定该方法,直到事件循环完成(我怎样才能知道这是什么时候?)?目前尚不清楚如何解决该问题。@user3735658图像提供程序中存在的
ForceAsynchronousImageLoading
标志使QML引擎创建一个工作线程,并在该线程中运行
requestXxx
。线程实例化已为您完成。@user3735658除非在下面运行它,否则您永远不会知道它调试程序并亲自查看它崩溃的确切位置。不要问我们:)