Heim > Backend-Entwicklung > C++ > Wie führe ich Funktoren oder Lambdas in einem bestimmten Thread in Qt aus, ähnlich wie bei GCD?

Wie führe ich Funktoren oder Lambdas in einem bestimmten Thread in Qt aus, ähnlich wie bei GCD?

DDD
Freigeben: 2024-12-20 04:37:13
Original
106 Leute haben es durchsucht

How to Execute Functors or Lambdas in a Given Thread in Qt, Similar to GCD?

Wie führe ich einen Funktor oder ein Lambda in einem bestimmten Thread in Qt, GCD-Stil aus?

In ObjC mit GCD gibt es eine Möglichkeit, ein Lambda auszuführen in einem der Threads, die eine Ereignisschleife drehen. Zum Beispiel:

dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
Nach dem Login kopieren
Nach dem Login kopieren

oder:

dispatch_async(dispatch_get_main_queue(), ^{ /* do sth */ });
Nach dem Login kopieren

Es führt etwas aus (entspricht []{ /etwas tun / } in C) im Hauptthread Warteschlange, entweder blockierend oder asynchron.

Wie kann ich das Gleiche tun? Qt?

Nach dem, was ich gelesen habe, schätze ich, dass die Lösung irgendwie darin bestünde, ein Signal an ein Objekt des Hauptthreads zu senden. Aber welches Objekt? Nur QApplication::instance()? (Das ist zu diesem Zeitpunkt das einzige Objekt, das im Hauptthread lebt.) Und welches Signal?

Lösung

Es ist durchaus möglich. Jede Lösung konzentriert sich auf die Bereitstellung eines Ereignisses, das den Funktor in ein Verbraucherobjekt einschließt, das sich im gewünschten Thread befindet. Wir werden diese Operation Metacall-Posting nennen. Die Angaben können auf verschiedene Arten ausgeführt werden.

Qt 5.10 und höher TL;DR

// invoke on the main thread
QMetaObject::invokeMethod(qApp, []{ ... });

// invoke on an object's thread
QMetaObject::invokeMethod(obj, []{ ... });

// invoke on a particular thread
QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(thread),
                         []{ ... });
Nach dem Login kopieren

TL;DR für Funktoren

// https://github.com/KubaO/stackoverflown/tree/master/questions/metacall-21646467

// Qt 5.10 & up - it's all done

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 - preferred, has least allocations

namespace detail {
template <typename F>
struct FEvent : public QEvent {
   using Fun = typename std::decay<F>::type;
   Fun fun;
   FEvent(Fun &amp;&amp; fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
   FEvent(const Fun &amp; fun) : QEvent(QEvent::None), fun(fun) {}
   ~FEvent() { fun(); }
}; }

template <typename F>
static void postToObject(F &amp;&amp; fun, QObject * obj = qApp) {
   if (qobject_cast<QThread*>(obj))
      qWarning() << "posting a call to a thread object - consider using postToThread";
   QCoreApplication::postEvent(obj, new detail::FEvent<F>(std::forward<F>(fun)));
}

template <typename F>
static void postToThread(F &amp;&amp; fun, QThread * thread = qApp->thread()) {
   QObject * obj = QAbstractEventDispatcher::instance(thread);
   Q_ASSERT(obj);
   QCoreApplication::postEvent(obj, new detail::FEvent<F>(std::forward<F>(fun)));
}
Nach dem Login kopieren
void test1() {
   QThread t;
   QObject o;
   o.moveToThread(&amp;t);

   // Execute in given object's thread
   postToObject([&amp;]{ o.setObjectName("hello"); }, &amp;o);
   // or
   postToObject(std::bind(&amp;QObject::setObjectName, &amp;o, "hello"), &amp;o);

   // Execute in given thread
   postToThread([]{ qDebug() << "hello from worker thread"; });

   // Execute in the main thread
   postToThread([]{ qDebug() << "hello from main thread"; });
}
Nach dem Login kopieren

TL; DR für Methoden/Slots

// Qt 5/4
template <typename T, typename R>
static void postToObject(T * obj, R(T::* method)()) {
   struct Event : public QEvent {
      T * obj;
      R(T::* method)();
      Event(T * obj, R(T::*method)()):
         QEvent(QEvent::None), obj(obj), method(method) {}
      ~Event() { (obj->*method)(); }
   };
   if (qobject_cast<QThread*>(obj))
      qWarning() << "posting a call to a thread object - this may be a bug";
   QCoreApplication::postEvent(obj, new Event(obj, method));
}

void test2() {
   QThread t;
   struct MyObject : QObject { void method() {} } obj;
   obj.moveToThread(&amp;t);

   // Execute in obj's thread
   postToObject(&amp;obj, &amp;MyObject::method);
}
Nach dem Login kopieren

Was ist mit einem einzelnen Schuss? Timer?

Alle oben genannten Methoden funktionieren mit Threads, die keine Ereignisschleife haben. Aufgrund von QTBUG-66458 erfordert die praktische Verwendung von QTimer::singleShot auch eine Ereignisschleife im Quellthread. Dann wird postToObject sehr einfach, und Sie könnten möglicherweise einfach QTimer::singleShot direkt verwenden, obwohl es ein umständlicher Name ist, der die Absicht für diejenigen verbirgt, die mit dieser Redewendung nicht vertraut sind. Die Umleitung über eine Funktion mit dem Namen, um die Absicht besser anzuzeigen, ist sinnvoll, auch wenn Sie die Typprüfung nicht benötigen:

template <typename F>
static void postToObject(F &amp;&amp; fun, QObject * obj = qApp) {
   if (qobject_cast<QThread*>(obj))
      qWarning() << "posting a call to a thread object - consider using postToThread";
   QTimer::singleShot(0, obj, std::forward<F>(fun));
}
Nach dem Login kopieren

Common Code

Lassen Sie uns unser Problem anhand von definieren Befolgen Sie den allgemeinen Code. Die einfachsten Lösungen posten das Ereignis entweder an das Anwendungsobjekt, wenn der Ziel-Thread der Haupt-Thread ist, oder an einen Event-Dispatcher für einen anderen gegebenen Thread. Da der Event-Dispatcher erst existiert, nachdem QThread::run eingegeben wurde, geben wir die Anforderung für die Ausführung des Threads an, indem wir „true“ von „needsRunningThread“ zurückgeben.

#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
Nach dem Login kopieren

Die Metacall-Posting-Funktionen in ihrer einfachsten Form erfordern, dass der Funktoraufrufkonsument ein Objekt für einen bestimmten Thread bereitstellt und das Funktoraufrufereignis instanziiert. Die Implementierung des Ereignisses liegt noch vor uns und ist der wesentliche Unterschied zwischen verschiedenen Implementierungen.

#ifndef HAS_POSTMETACALL
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));
}
#endif
Nach dem Login kopieren

Zu Demonstrationszwecken sendet der Arbeitsthread zunächst einen Metaaufruf an den Hauptthread und übergibt ihn dann an QThread ::run(), um eine Ereignisschleife zu starten, um auf mögliche Metaaufrufe von anderen Threads zu warten. Ein Mutex wird verwendet, um dem Thread-Benutzer auf einfache Weise zu ermöglichen, auf den Start des Threads zu warten, wenn dies durch die Implementierung des Verbrauchers erforderlich ist. Eine solche Wartezeit ist für den oben angegebenen Standard-Ereigniskonsumenten erforderlich.

dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
Nach dem Login kopieren
Nach dem Login kopieren

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

Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage