Hoe voer je een functor of een lambda uit in een bepaalde thread in Qt, GCD-stijl?

In ObjC met GCD is er een manier om een ​​lambda uit te voeren in een van de threads die een gebeurtenislus draaien. Bijvoorbeeld:

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

of:

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

Het voert iets uit (gelijk aan []{ /* do sth */ }in C++) in de wachtrij van de hoofdthread, ofwel blokkerend of asynchroon.

Hoe kan ik hetzelfde doen in Qt?

Van wat ik heb gelezen, denk ik dat de oplossing op de een of andere manier zou zijn om een ​​signaal te sturen naar een object van de hoofdthread. Maar welk voorwerp? Gewoon QApplication::instance()? (Dat is het enige object dat op dat moment in de rode draad leeft.) En welk signaal?


Op basis van de huidige antwoorden en mijn huidige onderzoek, lijkt het erop dat ik een dummy-object nodig heb om in de hoofdthread te zitten met een slot dat wacht om wat code binnen te krijgen om uit te voeren.

Dus heb ik besloten om QApplicationte subclasseren om dat toe te voegen. Mijn huidige code, die niet werkt (maar misschien kunnen jullie helpen):

#include <QApplication>
#include <QThread>
#include <QMetaMethod>
#include <functional>
#include <assert.h>
class App : public QApplication
{
    Q_OBJECT
public:
    App();
signals:
public slots:
    void genericExec(std::function<void(void)> func) {
        func();
    }
private:
    // cache this
    QMetaMethod genericExec_method;
public:
    void invokeGenericExec(std::function<void(void)> func, Qt::ConnectionType connType) {
        if(!genericExec_method) {
            QByteArray normalizedSignature = QMetaObject::normalizedSignature("genericExec(std::function<void(void)>)");
            int methodIndex = this->metaObject()->indexOfSlot(normalizedSignature);
            assert(methodIndex >= 0);
            genericExec_method = this->metaObject()->method(methodIndex);
        }
        genericExec_method.invoke(this, connType, Q_ARG(std::function<void(void)>, func));
    }
};
static inline
void execInMainThread_sync(std::function<void(void)> func) {
    if(qApp->thread() == QThread::currentThread())
        func();
    else {
        ((App*) qApp)->invokeGenericExec(func, Qt::BlockingQueuedConnection);
    }
}
static inline
void execInMainThread_async(std::function<void(void)> func) {
    ((App*) qApp)->invokeGenericExec(func, Qt::QueuedConnection);
}

Antwoord 1, autoriteit 100%

Het is zeker mogelijk. Elke oplossing is gericht op het leveren van een gebeurtenis die de functor omhult met een consumentenobject dat zich in de gewenste thread bevindt. We zullen deze bewerking metacall-posting noemen. De bijzonderheden kunnen op verschillende manieren worden uitgevoerd.

Qt 5.10 & omhoog 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),
                         []{ ... });

TL;DR voor functors

// 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 &&fun, QObject *obj = qApp) {
  QMetaObject::invokeMethod(obj, std::forward<F>(fun));
}
template <typename F>
static void postToThread(F && 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 && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
   FEvent(const Fun & fun) : QEvent(QEvent::None), fun(fun) {}
   ~FEvent() { fun(); }
}; }
template <typename F>
static void postToObject(F && 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 && fun, QThread * thread = qApp->thread()) {
   QObject * obj = QAbstractEventDispatcher::instance(thread);
   Q_ASSERT(obj);
   QCoreApplication::postEvent(obj, new detail::FEvent<F>(std::forward<F>(fun)));
}
// Qt 5 - alternative version
template <typename F>
static void postToObject2(F && fun, QObject * obj = qApp) {
   if (qobject_cast<QThread*>(obj))
      qWarning() << "posting a call to a thread object - consider using postToThread";
   QObject src;
   QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun),
                    Qt::QueuedConnection);
}
template <typename F>
static void postToThread2(F && fun, QThread * thread = qApp->thread()) {
   QObject * obj = QAbstractEventDispatcher::instance(thread);
   Q_ASSERT(obj);
   QObject src;
   QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun),
                    Qt::QueuedConnection);
}
void test1() {
   QThread t;
   QObject o;
   o.moveToThread(&t);
   // Execute in given object's thread
   postToObject([&]{ o.setObjectName("hello"); }, &o);
   // or
   postToObject(std::bind(&QObject::setObjectName, &o, "hello"), &o);
   // Execute in given thread
   postToThread([]{ qDebug() << "hello from worker thread"; });
   // Execute in the main thread
   postToThread([]{ qDebug() << "hello from main thread"; });
}

TL;DR voor 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(&t);
   // Execute in obj's thread
   postToObject(&obj, &MyObject::method);
}

TL;DR: Hoe zit het met een timer voor één opname?

Alle bovenstaande methoden werken vanuit threads die geen gebeurtenislus hebben. Dankzij QTBUG-66458, is de handige toe-eigening van QTimer::singleShotheeft ook een gebeurtenislus in de bronthread nodig. Dan wordt postToObjectheel eenvoudig, en je zou eventueel gewoon QTimer::singleShotrechtstreeks kunnen gebruiken, hoewel het een onhandige naam is die de bedoeling verbergt voor degenen die niet bekend zijn met dit idioom. De indirectheid via een functie met de naam om de intentie beter aan te geven, is logisch, zelfs als je de typecontrole niet nodig hebt:

template <typename F>
static void postToObject(F && 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));
}

Algemene code

Laten we ons probleem definiëren in termen van de volgende algemene code. De eenvoudigste oplossingen zullen de gebeurtenis naar het toepassingsobject posten, als de doelthread de hoofdthread is, of naar een gebeurtenisdispatcher voor een andere gegeven thread. Aangezien de gebeurtenisverzender pas bestaat nadat QThread::runis ingevoerd, geven we aan dat de thread moet worden uitgevoerd door true te retourneren vanuit needsRunningThread.

#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

De metacall-postfuncties, in hun eenvoudigste vorm, vereisen dat de functor-call-consument een object voor een bepaalde thread levert en de functor-call-gebeurtenis instantiëren. De uitvoering van het evenement ligt nog voor ons, en is het essentiële verschil tussen verschillende uitvoeringen.

De tweede overbelasting neemt een rvalue-referentie voor de functor, waardoor mogelijk een kopieerbewerking op de functor wordt opgeslagen. Dit is handig als het vervolg gegevens bevat die duur zijn om te kopiëren.

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

Voor demonstratiedoeleinden plaatst de worker-thread eerst een metacall naar de hoofdthread en gaat dan verder met QThread::run()om een ​​gebeurtenislus te starten om te luisteren naar mogelijke metacalls van andere threads. Een mutex wordt gebruikt om de gebruiker van de thread in staat te stellen op een eenvoudige manier te wachten tot de thread begint, indien de implementatie van de consument dit vereist. Dergelijk wachten is nodig voor de standaardgebeurtenisconsument die hierboven is gegeven.

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

Ten slotte starten we de bovenstaande werkthread die een metacall plaatst naar de hoofdthread (toepassingsthread), en de applicatiethread plaatst een metacall naar de worker-thread.

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

De uitvoer ziet er in alle implementaties ongeveer als volgt uit. De functors kruisen de threads: degene die in de hoofdthread is gemaakt, wordt uitgevoerd in de worker-thread en omgekeerd.

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

Qt 5-oplossing met een tijdelijk object als signaalbron

De eenvoudigste benadering voor Qt 5 is om een ​​tijdelijke QObjectals signaalbron te gebruiken en de functor te verbinden met zijn destroyed(QObject*)-signaal. Wanneer postMetaCallterugkeert, wordt de signalSourcevernietigd, zendt het zijn destroyed-signaal uit en post de metacall naar het proxy-object.

Dit is misschien wel de meest beknopte en rechttoe rechtaan implementatie in de C++11-stijl. Het object signalSourcewordt op de C++11 RAII-manier gebruikt voor de bijwerkingen van zijn vernietiging. De uitdrukking “bijwerkingen” heeft een betekenis binnen de semantiek van C++11 en mag niet worden geïnterpreteerd als “onbetrouwbaar” of “ongewenst” – dat is allesbehalve. Het contract van QObjectmet ons is om ergens tijdens de uitvoering van zijn destructor destroyeduit te zenden. We zijn meer dan welkom om dat feit te gebruiken.

#include <QtCore>
#include <functional>
namespace FunctorCallConsumer { QObject * forThread(QThread*); }
#define HAS_POSTMETACALL
void postMetaCall(QThread * thread, const std::function<void()> & fun) {
   QObject signalSource;
   QObject::connect(&signalSource, &QObject::destroyed,
                    FunctorCallConsumer::forThread(thread), [=](QObject*){ fun(); });
}
#ifdef __cpp_init_captures
void postMetaCall(QThread * thread, std::function<void()> && fun) {
   QObject signalSource;
   QObject::connect(&signalSource, &QObject::destroyed,
                    FunctorCallConsumer::forThread(thread), [fun(std::move(fun))](QObject*){ fun(); });
}
#endif
// Common Code follows here

Als we alleen van plan zijn om in de hoofdthread te posten, wordt de code bijna triviaal:

void postToMainThread(const std::function<void()> & fun) {
  QObject signalSource;
  QObject::connect(&signalSource, &QObject::destroyed, qApp, [=](QObject*){
    fun();
  });
}
#ifdef __cpp_init_captures
void postToMainThread(std::function<void()> && fun) {
  QObject signalSource;
  QObject::connect(&signalSource, &QObject::destroyed, qApp, [fun(std::move(fun))](QObject*){
    fun();
  });
}
#endif

Qt 4/5-oplossing met behulp van QEvent Destructor

Dezelfde aanpak kan rechtstreeks worden toegepast op QEvent. De virtuele destructor van de gebeurtenis kan de functor aanroepen. De gebeurtenissen worden verwijderd direct nadat ze zijn afgeleverd door de gebeurtenisverzender van de thread van het consumentenobject, zodat ze altijd in de juiste thread worden uitgevoerd. Dit verandert niet in Qt 4/5.

#include <QtCore>
#include <functional>
class FunctorCallEvent : public QEvent {
   std::function<void()> m_fun;
   QThread * m_thread;
public:
   FunctorCallEvent(const std::function<void()> & fun, QObject * receiver) :
      QEvent(QEvent::None), m_fun(fun), m_thread(receiver->thread()) {}
   FunctorCallEvent(std::function<void()> && fun, QObject * receiver) :
      QEvent(QEvent::None), m_fun(std::move(fun)), m_thread(receiver->thread()) { qDebug() << "move semantics"; }
   ~FunctorCallEvent() {
      if (QThread::currentThread() == m_thread)
         m_fun();
      else
         qWarning() << "Dropping a functor call destined for thread" << m_thread;
   }
};
// Common Code follows here

Om alleen in de hoofdthread te posten, wordt het nog eenvoudiger:

class FunctorCallEvent : public QEvent {
   std::function<void()> m_fun;
public:
   FunctorCallEvent(const std::function<void()> & fun) :
      QEvent(QEvent::None), m_fun(fun) {}
   FunctorCallEvent(std::function<void()> && fun, QObject * receiver) :
      QEvent(QEvent::None), m_fun(std::move(fun)) {}
   ~FunctorCallEvent() {
      m_fun();
   }
};
void postToMainThread(const std::function<void()> & fun) {
   QCoreApplication::postEvent(qApp, new FunctorCallEvent(fun);
}
void postToMainThread(std::function<void()> && fun) {
   QCoreApplication::postEvent(qApp, new FunctorCallEvent(std::move(fun)));
}

Qt 5-oplossing met behulp van de privé QMetaCallEvent

De functor kan worden ingepakt in de Qt 5 slot-objectlading van de QMetaCallEvent. De functor wordt aangeroepen door QObject::eventen kan dus naar elk object in de doelthread worden gepost. Deze oplossing gebruikt de privé-implementatiedetails van Qt 5.

#include <QtCore>
#include <private/qobject_p.h>
#include <functional>
class FunctorCallEvent : public QMetaCallEvent {
public:
   template <typename Functor>
   FunctorCallEvent(Functor && fun, QObject * receiver) :
      QMetaCallEvent(new QtPrivate::QFunctorSlotObject<Functor, 0, typename QtPrivate::List_Left<void, 0>::Value, void>
                     (std::forward<Functor>(fun)), receiver, 0, 0, 0, (void**)malloc(sizeof(void*))) {}
   // Metacalls with slot objects require an argument array for the return type, even if it's void.
};
// Common Code follows here

Qt 4/5-oplossing met een aangepaste gebeurtenis en consument

We implementeren de event()methode van het object opnieuw en laten het de functor aanroepen. Dit vraagt ​​om een ​​expliciet gebeurtenisconsumentenobject in elke thread waarnaar de functors worden gepost. Het object wordt opgeschoond wanneer de thread is voltooid, of, voor de hoofdthread, wanneer de toepassingsinstantie wordt vernietigd. Het werkt op zowel Qt 4 als Qt 5. Het gebruik van rvalue-referenties vermijdt het kopiëren van de tijdelijke functor.

#include <QtCore>
#include <functional>
class FunctorCallEvent : public QEvent {
   std::function<void()> m_fun;
public:
   FunctorCallEvent(const std::function<void()> & fun, QObject *) :
      QEvent(QEvent::None), m_fun(fun) {}
   FunctorCallEvent(std::function<void()> && fun, QObject *) :
      QEvent(QEvent::None), m_fun(std::move(fun)) { qDebug() << "move semantics"; }
   void call() { m_fun(); }
};
#define HAS_FUNCTORCALLCONSUMER
class FunctorCallConsumer : public QObject {
   typedef QMap<QThread*, FunctorCallConsumer*> Map;
   static QObject * m_appThreadObject;
   static QMutex m_threadObjectMutex;
   static Map m_threadObjects;
   bool event(QEvent * ev) {
      if (!dynamic_cast<FunctorCallEvent*>(ev)) return QObject::event(ev);
      static_cast<FunctorCallEvent*>(ev)->call();
      return true;
   }
   FunctorCallConsumer() {}
   ~FunctorCallConsumer() {
      qDebug() << "consumer done for thread" << thread();
      Q_ASSERT(thread());
      QMutexLocker lock(&m_threadObjectMutex);
      m_threadObjects.remove(thread());
   }
   static void deleteAppThreadObject() {
      delete m_appThreadObject;
      m_appThreadObject = nullptr;
   }
public:
   static bool needsRunningThread() { return false; }
   static FunctorCallConsumer * forThread(QThread * thread) {
      QMutexLocker lock(&m_threadObjectMutex);
      Map map = m_threadObjects;
      lock.unlock();
      Map::const_iterator it = map.find(thread);
      if (it != map.end()) return *it;
      FunctorCallConsumer * consumer = new FunctorCallConsumer;
      consumer->moveToThread(thread);
      if (thread != qApp->thread())
         QObject::connect(thread, SIGNAL(finished()), consumer, SLOT(deleteLater()));
      lock.relock();
      it = m_threadObjects.find(thread);
      if (it == m_threadObjects.end()) {
         if (thread == qApp->thread()) {
            Q_ASSERT(! m_appThreadObject);
            m_appThreadObject = consumer;
            qAddPostRoutine(&deleteAppThreadObject);
         }
         m_threadObjects.insert(thread, consumer);
         return consumer;
      } else {
         delete consumer;
         return *it;
      }
   }
};
QObject * FunctorCallConsumer::m_appThreadObject = nullptr;
QMutex FunctorCallConsumer::m_threadObjectMutex;
FunctorCallConsumer::Map FunctorCallConsumer::m_threadObjects;
// Common Code follows here

Antwoord 2, autoriteit 12%

Er is één nieuwe benadering die volgens mij de gemakkelijkste is.
Het is van Qt 5.4. Link naar documentatie

void QTimer::singleShot(int msec, const QObject *context, Functor functor)

Voorbeeld:

QTimer::singleShot(0, qApp, []()
{
    qDebug() << "hi from event loop";
});

lambda wordt uitgevoerd in qApp thread (hoofdthread). Je zou de context kunnen vervangen door elk QObject dat je wilt.

Bijgewerkt

QTimer heeft een gebeurtenislus nodig om te werken. Voor threads zonder qt-gebeurtenislus (std::thread) zouden we er een kunnen maken. Code om lambda in std::thread uit te voeren.

QEventLoop loop;
Q_UNUSED(loop)
QTimer::singleShot(0, qApp, []()
{
    qDebug() << "singleShot from std thread";
});

Volledig voorbeeld

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include <thread>
#include <QThread>
#include <QEventLoop>
#include <QThread>
using std::thread;
class TestObj
        :public QObject
{
// Used new connect syntax no need for Q_OBJECT define
// you SHOULD use it. I used just to upload one file
//Q_OBJECT
public slots:
    void doWork()
    {
        qDebug() << "QThread id" << QThread::currentThreadId();
        QTimer::singleShot(0, qApp, []()
        {
            qDebug() << "singleShot from QThread" << QThread::currentThreadId();
        });
    }
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main thread id" << QThread::currentThreadId();
    thread testThread([]()
    {
        QEventLoop loop;
        Q_UNUSED(loop)
        qDebug() << "std::thread id" << QThread::currentThreadId();
        QTimer::singleShot(0, qApp, []()
        {
            qDebug() << "singleShot from std thread" << QThread::currentThreadId();
        });
        qDebug() << "std::thread finished";
    });
    testThread.detach();
    QThread testQThread;
    TestObj testObj;
    testObj.moveToThread(&testQThread);
    QObject::connect(&testQThread, &QThread::started, &testObj, &TestObj::doWork);
    testQThread.start();
    return a.exec();
}

Antwoord 3, autoriteit 10%

Kan zoiets nuttigs zijn?

template <typename Func>
inline static void MyRunLater(Func func) {
    QTimer *t = new QTimer();
    t->moveToThread(qApp->thread());
    t->setSingleShot(true);
    QObject::connect(t, &QTimer::timeout, [=]() {
        func();
        t->deleteLater();
    });
    QMetaObject::invokeMethod(t, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}

Dit stukje code zorgt ervoor dat je lambda zo snel mogelijk in de hoofdthread-gebeurtenislus wordt uitgevoerd.
Geen ondersteuning voor argumenten, dit is een zeer eenvoudige code.

OPMERKING: ik heb het niet goed getest.


Antwoord 4

Anderen hebben uitstekende antwoorden. hier is mijn suggestie. in plaats van zoiets als dit te doen:-

QMetaObject::invokeMethod(socketManager,"newSocket",
                          Qt::QueuedConnection,
                          Q_ARG(QString, host),
                          Q_ARG(quint16, port.toUShort()),
                          Q_ARG(QString, username),
                          Q_ARG(QString, passhash)
                          );

doe zoiets wat leuker is:-

QMetaObject::invokeMethod(socketManager,[=](){
    socketManager->newSocket(host,port.toUShort(),username,passhash);
},Qt::QueuedConnection);

Antwoord 5

Ik heb absoluut geen idee waar je het over hebt, maar ik ga proberen om het op de een of andere manier te beantwoorden.

Stel dat je een klas hebt met een slotfunctie

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass() {}
public slots: 
    void MySlot() { qDebug() << "RAWR";
};

Dus nee als je dit synchroon in de hoofdthread wilt laten lopen, kun je die functie direct aanroepen. Om een ​​signaal aan te sluiten, moet je een object maken en een signaal op de sleuf aansluiten.

class MySignalClass : public QObject
{
    Q_OBJECT
public:
    MySignalClass() {}
    signalSomthign() { emit someAwesomeSignal; }
public signals: 
    void someAwesomeSignal();
};

En ergens in de hoofdthread doe je zoiets als

MyClass slotClass;
MySignalClass signalClass;
qobject::connect(&signalClass, SIGNAL(someAwesomeSignal), &slotClass(), SLOT(MySlot)));

Dus als er iets is, kun je meerdere signalen op dat slotobject aansluiten, maar realistisch gezien zal de code die ik heb verstrekt niet anders zijn dan een normale functieaanroep. Dat kun je zien met een stacktracering. Als u de vlag qobject::queuedConneciton toevoegt aan de verbinding, wordt de slotaanroep in de gebeurtenislus in de wachtrij geplaatst.

U kunt ook gemakkelijk een signaal doorvoeren, maar dit wordt automatisch een wachtrijverbinding

MyClass slotClass;
MySignalClass signalClass;
QThread someThread;
slotClass.moveToThread(&someThread);
qobject::connect(&signalClass, SIGNAL(someAwesomeSignal), &slotClass(), SLOT(MySlot)));

Nu heb je in principe een schroefdraadsignaal. Als uw signaal wordt gethread, hoeft u alleen maar over te schakelen naar signalClass.moveToThread(&someThread), wanneer het signaal wordt uitgezonden signalClass wordt uitgevoerd in de hoofdthread.

Als je niet wilt dat een object wordt aangeroepen, weet ik niet zeker of lamdas zou kunnen werken. Ik heb ze eerder gebruikt, maar ik denk dat ze nog steeds in een klas moeten worden verpakt.

qobject::connect(&signalClass, &slotClass::MySlot, [=]() { /* whatever */ });

Hoewel ik er vrij zeker van ben dat je met Qt5 zelfs zo ver kunt gaan als het creëren van een slot in lijn binnen een verbinding. Maar als je eenmaal lambda’s gebruikt, heb ik geen idee hoe threads ermee werken. Voor zover ik weet, heb je een object nodig om in een thread te zitten om het slot vanuit de hoofdthread te forceren.

Other episodes