Maison >développement back-end >C++ >Comment exécuter des foncteurs ou des Lambdas dans un thread spécifique dans Qt ?

Comment exécuter des foncteurs ou des Lambdas dans un thread spécifique dans Qt ?

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2024-12-21 10:45:08632parcourir

How to Execute Functors or Lambdas in a Specific Thread in Qt?

Exécution de foncteurs ou de Lambdas dans un thread donné dans Qt, style GCD

Problème :

En Objective-C avec GCD, on peut exécuter un lambda ou une fonction dans n'importe quel thread qui fait tourner une boucle d'événement. Par exemple, la distribution de quelque chose dans la file d'attente du thread principal peut être effectuée de manière synchrone ou asynchrone :

dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });

dispatch_async(dispatch_get_main_queue(), ^{ /* do sth */ });

Comment cela peut-il être réalisé dans Qt ?

Solution :

Qt 5.10 et versions ultérieures

Pour Qt 5.10 et versions ultérieures, la solution la plus simple est d'utiliser QMetaObject::invokeMethod:

QMetaObject::invokeMethod(qApp, []{ ... });

QMetaObject::invokeMethod(obj, []{ ... });

QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(thread),
                         []{ ... });

Pour les foncteurs :

template <typename F>
static void postToObject(F &amp;&amp;fun, QObject *obj = qApp) {
  QMetaObject::invokeMethod(obj, std::forward<F>(fun));
}

template <typename F>
static void postToThread(F &amp;&amp; fun, QThread *thread = qApp->thread()) {
   auto *obj = QAbstractEventDispatcher::instance(thread);
   Q_ASSERT(obj);
   QMetaObject::invokeMethod(obj, std::forward<F>(fun));
}

Qt 5/4

Pour Qt 5/4, il en existe plusieurs méthodes :

  • Publication via QEvent : Créez un événement personnalisé FunctorCallEvent et implémentez un objet consommateur FunctorCallConsumer.
  • Publication via un objet temporaire : Utilisez un QObject temporaire comme source de signal et connectez le foncteur à son destroy() signal.
  • Publication via QMetaCallEvent (Qt 5 Private) : Enveloppez le foncteur dans le QtPrivate::QFunctorSlotObject privé et utilisez un QMetaCallEvent.
  • Publication via Object Consommateur d'événements : Remplacez la méthode event() d'un objet pour appeler le foncteur.

Code commun :

#ifndef HAS_FUNCTORCALLCONSUMER
namespace FunctorCallConsumer {
   bool needsRunningThread() { return true; }
   QObject * forThread(QThread * thread) {
      Q_ASSERT(thread);
      QObject * target = thread == qApp->thread()
            ? static_cast<QObject*>(qApp) : QAbstractEventDispatcher::instance(thread);
      Q_ASSERT_X(target, "postMetaCall", "the receiver thread must have an event loop");
      return target;
   }
}
#endif

void postMetaCall(QThread * thread, const std::function<void()> &amp; fun) {
   auto receiver = FunctorCallConsumer::forThread(thread);
   QCoreApplication::postEvent(receiver, new FunctorCallEvent(fun, receiver));
}

void postMetaCall(QThread * thread, std::function<void()> &amp;&amp; fun) {
   auto receiver = FunctorCallConsumer::forThread(thread);
   QCoreApplication::postEvent(receiver,
                               new FunctorCallEvent(std::move(fun), receiver));
}

Démonstration :

class Worker : public QThread {
   QMutex m_started;
   void run() {
      m_started.unlock();
      postMetaCall(qApp->thread(), []{
         qDebug() << "worker functor executes in thread" << QThread::currentThread();
      });
      QThread::run();
   }
public:
   Worker(QObject * parent = 0) : QThread(parent) { m_started.lock(); }
   ~Worker() { quit(); wait(); }
   void waitForStart() { m_started.lock(); m_started.unlock(); }
};

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   a.thread()->setObjectName("main");
   Worker worker;
   worker.setObjectName("worker");
   qDebug() << "worker thread:" << &amp;worker;
   qDebug() << "main thread:" << QThread::currentThread();
   if (FunctorCallConsumer::needsRunningThread()) {
      worker.start();
      worker.waitForStart();
   }
   postMetaCall(&amp;worker, []{ qDebug() << "main functor executes in thread" << QThread::currentThread(); });
   if (!FunctorCallConsumer::needsRunningThread()) worker.start();
   QMetaObject::invokeMethod(&amp;a, "quit", Qt::QueuedConnection);
   return a.exec();
}

Sortie :

worker thread: QThread(0x7fff5692fc20, name = "worker")
main thread: QThread(0x7f86abc02f00, name = "main")
main functor executes in thread QThread(0x7fff5692fc20, name = "worker")
worker functor executes in thread QThread(0x7f86abc02f00, name = "main")

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn