Heim >Backend-Entwicklung >C++ >Wie führe ich Funktoren oder Lambdas in einem bestimmten Thread in Qt aus?

Wie führe ich Funktoren oder Lambdas in einem bestimmten Thread in Qt aus?

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2024-12-21 10:45:08670Durchsuche

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

Ausführen von Funktoren oder Lambdas in einem gegebenen Thread in Qt, GCD-Stil

Problem:

In Objective-C Mit GCD kann man ein Lambda oder eine Funktion in jedem Thread ausführen, der eine Ereignisschleife dreht. Beispielsweise kann das Senden von etwas an die Warteschlange des Hauptthreads synchron oder asynchron erfolgen:

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

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

Wie kann dies in Qt erreicht werden?

Lösung:

Qt 5,10 und höher

Für Qt 5.10 und höher ist die einfachste Lösung die Verwendung von QMetaObject::invokeMethod:

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

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

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

Für Funktoren:

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

Für Qt 5/4 gibt es mehrere Methoden:

  • Posten über QEvent: Erstellen Sie ein benutzerdefiniertes Ereignis FunctorCallEvent und implementieren Sie ein Verbraucherobjekt FunctorCallConsumer.
  • Posten über temporäres Objekt: Verwenden Sie ein temporäres QObject als Signalquelle und verbinden Sie den Funktor mit seinem destroy() Signal.
  • Posten über QMetaCallEvent (Qt 5 Private):Verpacken Sie den Funktor in das private QtPrivate::QFunctorSlotObject und verwenden Sie ein QMetaCallEvent.
  • Posten über Objekt Ereigniskonsument: Überschreiben Sie die event()-Methode eines Objekts, um das aufzurufen Funktor.

Common Code:

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

Demonstration:

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

Ausgabe:

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

Das obige ist der detaillierte Inhalt vonWie führe ich Funktoren oder Lambdas in einem bestimmten Thread in Qt aus?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn