【问题标题】:What's the proper way to construct a signal/slot wrapper to a blocking call?为阻塞调用构造信号/槽包装器的正确方法是什么?
【发布时间】:2015-09-10 17:11:25
【问题描述】:

假设我有一个QObject 和一个阻塞方法(比如说,它是一个库调用,需要在返回之前从网络中获取大量数据)。

class Foo : public QObject { 
Bar* _bar;
public:
    // non blocking call, emits stuffDone when done
    void startStuff(int a, int b);
signals:
    void stuffDone(int sum);
}


class Bar {
public:
    // Blocking call
    int doStuff(int a, b) { 
        for(int i=0; i<=100000000000; i++);
        return a + b;
    }
}

我希望我的 Foo::startStuff 方法在适当的(单独的)线程中运行 doStuff 并在完成时触发 stuffDone 信号。 startStuff 必须立即返回。

Bar可以在必要时成为 QObject,从而允许通过moveToThread设置线程亲和性

这样做的最简单和最惯用('Qt-like')的方式是什么?

【问题讨论】:

  • documentation to QThread 似乎为您的问题提供了两个简单的答案。你有什么理由不能使用这些吗?
  • @Rostislav 该文档并未提供完整的图片。手动实例化线程,而不是使用线程池,通常是一个坏主意。线程很昂贵。
  • 要异步运行直到完成,您永远不需要使用显式线程 - 您应该使用线程池。
  • @KubaOber 线程池很棒——我自己在我的应用程序中使用它们。但是,我不同意总是使用它们。如果线程大部分时间都花在等待 I/O 上——最好不要将它放入线程池中——操作系统会更好地调度它。然而,线程池不会让其他任务在前一个任务结束之前运行(如果池有更多线程 - 那么等待 I/O 的 N 个任务将阻止下一个任务运行)。对于这样的任务,QFuture 确实会更好。对于计算密集型任务 - 线程池更好。
  • @Rostislav 我认为你不应该在启动之后创建线程..

标签: c++ qt asynchronous signals blocking


【解决方案1】:

QtConcurrent::run 可能是最惯用的:

struct Bar {
   // Blocks for 3 seconds
   int doStuff(int a, b) { 
      QThread::sleep(3);
      return a+b+42;
   }
};

class Foo : public QObject {
   Q_OBJECT
   Bar _bar;
public:
   // Non-blocking, emits stuffDone when done
   void startStuff(int a, int b) {
      QtConcurrent::run([a,b,this]{
         auto result = _bar.doStuff(a,b);
         emit stuffDone(result);
      });
   }
   Q_SIGNAL void stuffDone(int sum);
};

您也可以使用QFutureWatcher,而不是使用自定义Foo 类,但恕我直言,它更麻烦,因为没有提供结果的信号 - 您需要连接一个适用于结果的函子。

QSharedPointer<Bar> bar { new Bar };
auto watcher = new QFutureWatcher<int>;
connect(watcher, &QFutureWatcher::finished, watcher, [watcher, bar]{
  watcher->deleteLater();
  int result = watcher->result();
  // use the result here
});
auto future = QtConcurrent::run(&Bar::doStuff, bar, 1, 2);
watcher->setFuture(future);

请注意,“长”加法循环通常会被优化掉,因为它没有副作用,因此是死代码。如果要模拟阻塞,请使用QThread::[|m|u]sleep

【讨论】:

  • 谢谢。事实上,我的 Foo 也做了很多 other 的事情。我想知道,auto 有什么特别的原因吗?我宁愿留在 C++98 模式。
  • @TobiaTesan 如果没有 lambda,就无法编写健全的 Qt 5 代码,因此您确实需要 C++11。从 Windows 上的 MSVC2010 开始支持它们,并且在此之前的大多数其他编译器都支持它们。使用 Qt 5 保持 C++98 模式会迫使您进行比必要更多的输入 - 例如您必须声明方法而不是匿名函子等。
  • 谢谢。 @Future Reader:请记得CONFIG += c++11添加到您的.pro文件中,以免您浪费一整个上午试图找出问题...我刚做了。
  • @TobiaTesan 不过,这仅在 Unices 上是必需的。
猜你喜欢
  • 2016-08-02
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-09
  • 2011-02-03
  • 1970-01-01
相关资源
最近更新 更多