为阻塞调用构造 signal/slot 包装器的正确方法是什么?

What's the proper way to construct a signal/slot wrapper to a blocking call?

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

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 必须立即 return。

Bar 如有必要, 可以是 QObject,从而允许通过 moveToThread

设置线程关联

最简单和最惯用的 ('Qt-like') 方法是什么?

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

除了使用自定义 Foo class,您还可以使用 QFutureWatcher,但恕我直言,它更麻烦,因为没有提供结果的信号 - 您需要连接一个作用于结果的函子。

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

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