Quelle est la manière appropriée de construire un wrapper de signal / emplacement pour un appel bloquant?

Supposons que j’ai un QObject et une méthode de blocage (par exemple, c’est un appel de bibliothèque qui doit extraire beaucoup de données du réseau avant de retourner).

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

J’aimerais que ma méthode Foo::startStuff exécute doStuff dans le thread approprié (séparé) et déclenche un signal stuffDone à la fin. startStuff devra revenir immédiatement.

Bar peut être un QObject si nécessaire, permettant ainsi de définir l’affinité de thread via moveToThread

Quel est le moyen le plus simple et le plus idiomatique (‘Qt-like’) de le faire?

QtConcurrent::run serait probablement le plus idiomatique:

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

Au lieu d’utiliser la classe Foo personnalisée, vous pouvez également utiliser un QFutureWatcher , mais à QFutureWatcher , il est plus lourd car aucun signal ne fournit le résultat – vous devez connecter un foncteur qui fonctionne sur le résultat.

 QSharedPointer bar { new Bar }; auto watcher = new QFutureWatcher; 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); 

Notez que la boucle d’ajout “longue” est généralement optimisée car elle n’a aucun effet secondaire et constitue donc un code mort. Si vous voulez simuler un blocage, utilisez QThread::[|m|u]sleep .