C++ 什么';构造阻塞调用的信号/插槽包装器的正确方法是什么?

C++ 什么';构造阻塞调用的信号/插槽包装器的正确方法是什么?,c++,qt,asynchronous,signals,blocking,C++,Qt,Asynchronous,Signals,Blocking,假设我有一个QObject和一个阻塞方法(比如,它是一个库调用,需要在返回之前从网络中获取大量数据) class Foo:public QObject{ 巴*巴; 公众: //非阻塞调用,完成时发出stuffDone 无效启动凝灰岩(内部a、内部b); 信号: 无效完成(整数和); } 分类栏{ 公众: //阻塞呼叫 intdostuff(inta,b){ for(inti=0;iQtConcurrent::run可能是最常用的: struct Bar { // Blocks for 3

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

class Foo:public QObject{
巴*巴;
公众:
//非阻塞调用,完成时发出stuffDone
无效启动凝灰岩(内部a、内部b);
信号:
无效完成(整数和);
}
分类栏{
公众:
//阻塞呼叫
intdostuff(inta,b){

for(inti=0;i
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
类,您还可以使用
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);
QSharedPointer bar{new bar};
自动观察者=新QFutureWatcher;
连接(观察者和QFutureWatcher::完成,观察者,[观察者,条]{
watcher->deleteLater();
int result=watcher->result();
//在这里使用结果
});
auto future=QtConcurrent::run(&Bar::doStuff,Bar,1,2);
观察者->设置未来(未来);

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

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
类,您还可以使用
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);
QSharedPointer bar{new bar};
自动观察者=新QFutureWatcher;
连接(观察者和QFutureWatcher::完成,观察者,[观察者,条]{
watcher->deleteLater();
int result=watcher->result();
//在这里使用结果
});
auto future=QtConcurrent::run(&Bar::doStuff,Bar,1,2);
观察者->设置未来(未来);

请注意,“长”加法循环通常是经过优化的,因为它没有副作用,因此是死代码sleep

似乎为您的问题提供了两个简单的答案。您有什么理由不能使用它们吗?@Rostislav该文档并没有提供全部信息。手动实例化线程而不是使用线程池通常是个坏主意。线程很昂贵。要异步运行到完成,您应该ldn永远不需要使用显式线程-线程池是您应该使用的。@KubaOber线程池很棒-我自己在应用程序中使用它们。但是,我不同意总是使用它们。如果线程将花费大部分时间等待I/O-这是不将其放入线程池的一个很好的选择-操作系统线程池甚至不会让其他任务在前一个任务结束之前运行(如果池中有更多线程,那么等待I/O的N个任务将阻止下一个任务运行)。对于这样的任务,QFuture确实会更好。对于计算密集型任务,线程池更好。@Rostislav我认为你根本不应该在启动后创建线程。。似乎给了你两个简单的问题答案。你为什么不能使用它们呢?@Rostislav文档没有给出ntire picture.dll.dll.dll中的线程是手动实例化线程,而不是使用线程池,这通常是个坏主意。线程很贵。要异步运行以完成任务,您不应该使用显式线程-线程池是您应该使用的。@KubaOber线程池很棒-我自己在应用程序中使用它们。How无论如何,我不同意总是使用它们。如果线程将花费大部分时间等待I/O—这是不将其放入线程池的一个很好的选择—操作系统将在调度它方面做得更好。然而,线程池甚至不会让其他任务在前一个任务结束之前运行(如果池中有更多线程,则等待I/O的N个任务将阻止下一个任务运行)。对于这样的任务,QFuture确实会更好。对于计算密集型任务,线程池会更好。@Rostislav我认为你根本不应该在启动后创建线程。。谢谢。事实上,我的Foo也做了很多其他事情。我想知道,
自动
有什么特别的原因吗?我宁愿留下来n C++98模式。@TobiaTesan没有lambdas就无法编写正常的Qt 5代码,因此您确实需要C++11。Windows上的MSVC2010以及之前的大多数编译器都支持这些代码。Qt 5保持在C++98模式会迫使您执行超出必要的键入操作—例如,您必须声明方法,而不是匿名函子,等等谢谢。@Future Reader:请记住将
CONFIG+=c++11
添加到你的
.pro
文件中,以免你像我刚才那样浪费一个上午的时间试图解决问题。@TobiaTesan这只在Unices上是必要的。谢谢。事实上,我的Foo还做了很多其他事情。我想知道,有什么特别的问题吗
自动的原因是什么?
?我宁愿保持在C++98模式。@TobiaTesan没有lambdas就无法编写正常的Qt 5代码,因此您确实需要C++11。Windows上的MSVC2010以及之前的大多数编译器都支持它们。使用Qt 5保持在C++98模式会迫使您执行超出必要的键入操作—例如,您必须这样做声明方法而不是匿名函子,等等。谢谢。@Future Reader:请记住将
CONFIG+=c++11
添加到
.pro
文件中,以免浪费整个上午的时间试图解决问题……就像我刚才做的那样。@TobiaTesan这只在Unices上是必要的。