How to execute a functor or a lambda in a given thread in Qt, GCD-style?
Asked Answered
Y

5

60

In ObjC with GCD, there is a way of executing a lambda in any of the threads that spin an event loop. For example:

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

or:

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

It executes something (equivalent to []{ /* do sth */ } in C++) in the main thread's queue, either blocking or asynchronously.

How can I do the same in Qt?

From what I have read, I guess the solution would be somehow to send a signal to some object of the main thread. But what object? Just QApplication::instance()? (That is the only object living in the main thread at that point.) And what signal?


From the current answers and my current research, it really seems that I need some dummy object to sit in the main thread with some slot which just waits to get in some code to execute.

So, I decided to subclass QApplication to add that. My current code, which doesn't work (but maybe you can help):

#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);
}
Yirinec answered 8/2, 2014 at 13:20 Comment(5)
Your question is very vague. What exactly do you need to do? You can schedule something to execute later, but as long as it's main thread - it will block this thread. Or are you calling it from a worker thread?Hildebrandt
@VioletGiraffe: Of course, just as in the GCD code, this only makes sense in another thread. I basically just want to translate that GCD code to Qt code.Yirinec
I have no idea what GCD is. But you can send signals to another thread in Qt. I think you need Qt::QueuedConnection as the last argument to connect, and it's done. But let's wait for an actual answer from someone who knows this stuff.Hildebrandt
@VioletGiraffe: I want to queue some code call (e.g. some C++11 lambda) to the main thread. How? I don't necessarily want something to connect and if I must do that as a work-around, I don't really know what to connect.Yirinec
I just mentioned GCD because for those familiar with it, it immediately makes clear what I want. :) But already in my second sentence, I explain what it does and what I want (queue some code execution to the main thread queue).Yirinec
S
136

It is certainly possible. Any solution will center on delivering an event that wraps the functor to a consumer object residing in the desired thread. We shall call this operation metacall posting. The particulars can be executed in several ways.

Qt 5.10 & up 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 for 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 for methods/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: What about a single shot timer?

All of the above methods work from threads that don't have an event loop. Due to QTBUG-66458, the handy appropriation of QTimer::singleShot needs an event loop in the source thread as well. Then postToObject becomes very simple, and you could possibly just use QTimer::singleShot directly, although it's an awkward name that hides the intent from those unfamiliar with this idiom. The indirection via a function named to better indicate the intent makes sense, even if you don't need the type check:

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

Common Code

Let's define our problem in terms of the following common code. The simplest solutions will post the event to either the application object, iff the target thread is the main thread, or to an event dispatcher for any other given thread. Since the event dispatcher will exist only after QThread::run has been entered, we indicate the requirement for the thread to be running by returning true from 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

The metacall posting functions, in their simplest form, require the functor call consumer to provide object for a given thread, and instantiate the functor call event. The implementation of the event is still ahead of us, and is the essential difference between various implementations.

The second overload takes a rvalue reference for the functor, potentially saving a copy operation on the functor. This is helpful if the continuation contains data that is expensive to copy.

#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

For demonstration purposes, the worker thread first posts a metacall to the main thread, and then defers to QThread::run() to start an event loop to listen for possible metacalls from other threads. A mutex is used to allow the thread user to wait in a simple fashion for the thread to start, if necessitated by the consumer's implementation. Such wait is necessary for the default event consumer given above.

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

Finally, we start the above worker thread that posts a metacall to the main (application) thread, and the application thread posts a metacall to the 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();
}

The output will look approximately as follows in all implementations. The functors cross the threads: the one created in the main thread is executed in the worker thread, and vice-versa.

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 Solution Using a Temporary Object as The Signal Source

The simplest approach for Qt 5 is to use a temporary QObject as a signal source, and connect the functor to its destroyed(QObject*) signal. When postMetaCall returns, the signalSource gets destructed, emits its destroyed signal, and posts the metacall to the proxy object.

This is perhaps the most concise and straightforward implementation in the C++11 style. The signalSource object is used in the C++11 RAII fashion for the side effects of its destruction. The phrase "side effects" has a meaning within C++11's semantics and should not be interpreted to mean "unreliable" or "undesirable" - it's anything but. QObject's contract with us is to emit destroyed sometime during the execution of its destructor. We're more than welcome to use that fact.

#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

If we only intend to post to the main thread, the code becomes almost trivial:

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 Solution Using QEvent Destructor

The same approach can be applied to QEvent directly. The event's virtual destructor can call the functor. The events are deleted right after they are delivered by the consumer object's thread's event dispatcher, so they always execute in the right thread. This will not change 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

To post to main thread only, things become even simpler:

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 Solution Using the Private QMetaCallEvent

The functor can be wrapped in the Qt 5 slot object payload of the QMetaCallEvent. The functor will be invoked by QObject::event, and thus can be posted to any object in the target thread. This solution uses the private implementation details of 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 Solution Using a Custom Event and Consumer

We reimplement the event() method of the object, and have it call the functor. This calls for an explicit event consumer object in each thread that the functors are posted to. The object is cleaned up when its thread is finished, or, for the main thread, when the application instance is destructed. It works on both Qt 4 and Qt 5. The use of rvalue references avoids copying of the temporary 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
Skiba answered 9/2, 2014 at 0:23 Comment(16)
Hm, can I be sure that Qt will never change the semantics in what thread the destructor of the event will be called? Is that even documented? Also, why do you think your approach is better than to use QMetaMethod::invoke as in my sketch?Yirinec
Sure it might look as if the destructor is probably called in the same thread. But I'm not sure if it's a good idea to bet on that when it is not documented that way. It also feels like kind of a hack to use the destructor to call the callback/functor. I was just wondering whether my sketch is sane. Also, QMetaMethod::invoke has a sane interface and does what I want without hacks but you seem to suggest that there are better ways and I don't really see why (but I'm new to Qt).Yirinec
std::function is relatively cheap. You don't really save much (I'm not sure if anything - which copy do you avoid actually?). Also, you add a lot of overhead by the proxy object (way more than std::function. -- "Since one desires control over functor's lifetime": Why? The functor is extremely cheap compared to the QEvent. -- "prevent needless copying": How much copying is there really? Also it doesn't matter because std::function is cheap to copy. -- Also, your code looks quite complicated now and I'm not sure that it is really faster.Yirinec
Sure, but std::function<void()> is about the same (depends on the STL implementation of course) and you just add another indirection with QEvent, which is bad for performance because of cache misses, etc. Also, when I just want to queue stuff to the main thread (what I basically need and asked for), you don't need all that automatic proxy object handling. I just like simple and fast solutions. :)Yirinec
@Yirinec I hope you realize that each and every signal-slot call is creating a QMetaCallEvent in the background - just like the code I've posted originally, only there's more of it. No matter what you do the only mechanism for cross-thread calls implemented by Qt involves posting an event instance to an event loop. Just trace the code that actually gets executed in your own approach in the debugger, it'll make you realize what's going on. Be prepared, since it's a lot of code that is running. Also, since a functor captures the variables, it can be potentially a very expensive thing to copy.Revest
@Yirinec A cross-thread signal-slot call with one argument does two extra mallocs, too :)Revest
Hi Kuba, thanks a lot for your solution. It worked great under linux and windows, but I have this mysterious crash on mac:Mosa
@Kuba ...continue of my previous comment (I hit enter accidentally): ...I have this mysterious crash on mac: bool FunctorCallConsumer::event(QEvent * ev) { if (!dynamic_cast<FunctorCallEvent*>(ev)) return QObject::event(ev); ...it crashes in the dynamic_cast. The event ev was properly created and not yet destroyed, so I don't have a clue what could have gone wrong there. I tested it rally well on linux and windows, so it could be a compiler/qt version/platform-specific issue. Now I use the solution with a single-shot timer by @alediaferia.Mosa
@Kuba I found out that the ev->type() in the crash is 22 which I didn't find in the documentation.Mosa
@Kuba I might have found a solution: Instead of the dynamic_cast, I can use QEvent::registerEventType() at startup to register a new event type, then I use that type in the constructor of FunctorCallEvent, and then instead of the dynamic_cast I just check the ev->type() and if it is the one I registered at startup, I can use static_cast. So the code that crashed before will become if (FunctorCallEvent_type != ev->type()) return QObject::event(ev); now, which does not crash anymore.Mosa
Actually, the destroyed() signal is (according to the documentation) emitted before the object is destroyed, so your solution works fine even if the lambda execution results in an exception being thrown somewhere.Krakau
@NikosC. You're correct if a direct connection is used. If the connection is queued and to a receiver in the same thread, then you're guaranteed that the object won't in fact exist. If the receiver is in another thread, then the notion of object's existence vanishes due to lack of synchronization - unless the connection is of a blocking queued type, that is.Revest
For completeness, you maybe should add to this answer: These are async solutions, right? I.e. when you call QMetaObject::invokeMethod, this will return immediately and then at some later point call the lambda/function in the corresponding thread, i.e. equivalent to dispatch_async, right? Is there also some easy solution to have it blocking, like dispatch_sync?Yirinec
@Yirinec I won't do that because it encourages blocking code, and that's the last thing we want. Those kludges usually outlast their authors. If you're doing anything cross-threaded that you think has to block, you should be instead running callback code in your own thread, triggered from the code that ran in the other thread. If you are able enough to make the blocking code perform well, then you'll know all you need to know about synchronization primitives, and are more than welcome to use them. A semaphore is what you'd be looking for.Revest
Well, at least you should clarify that the solution you are giving is non-blocking. But also, I asked for both cases (blocking and non-blocking) in my question.Yirinec
There is a rare case. The event dispatcher is not created before QThread's exec is called, therefore, the return value of QAbstractEventDispatcher::instance(thread) could be null and the user needs to check it.Calondra
C
15

There are one new approach that is the easiest I think. It`s from Qt 5.4. Link to documentation

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

Example:

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

lambda will be executed in qApp thread(main thread). You could replace context with any QObject you want.

Updated

QTimer needs event loop to work. For Threads with no qt event loop(std::thread) we could create one. Code to run lambda in std::thread.

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

Full example

#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();
}
Crandall answered 3/8, 2016 at 12:15 Comment(7)
This is much simpler than accepted answer. Can you provide some insights/benchmarks on its overhead?Integer
Well I have tried this solution and I am getting QObject::startTimer: Timers can only be used with threads started with QThread error. Is there any way to use this method in a non-Qt object code?Integer
@Mike: The code is running inside a std::thread. Isn't starTimer supposed to queue execution of the functor in the event loop of the provided context (here qApp), just like what invokeMethod does?Integer
@Integer , startTimer needs an event dispatcher to be associated with the thread that is called from, because it needs to registerTimer (in order to get notified when the timeout has passed). So it is not similar to what is done by QMetaObject::invokeMethod as the timeout is not always 0. . .Cesura
@Mike: So that's why it is throwing that warning. I wished they have optimized the 0 case and bypassed the queuing inside the caller.Integer
Yes, this approach work only in threads where event loop is present @Integer is correct.Crandall
QTimer::singleShot has a QTBUG-66458 in QSingleShotTimer. The implementation starts a timer in the current thread, then moves itself to the target thread. This ties it unnecessarily to the current thread's event loop, even though the timer will be moved to the target thread immediately as well.Revest
C
12

May something like this be any useful?

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

This piece of code will make your lambda run on the main thread event loop as soon as it is possible. No args support, this is a very basic code.

NOTE: I didn't test it properly.

Casto answered 7/6, 2014 at 8:18 Comment(1)
Works perfectly. Doesn't need to be templated. Just take std::function<void()> and let lambda creator handle argument capture, etc.Arabian
P
1

Others have exelent answers. here is my suggest. instead of doing something like this:-

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

do something like this which is more nice:-

QMetaObject::invokeMethod(socketManager,[=](){
    socketManager->newSocket(host,port.toUShort(),username,passhash);
},Qt::QueuedConnection);
Pageboy answered 6/2, 2020 at 21:16 Comment(0)
V
-2

I have absolutely no idea what your talking about, but I'm going to try to answer it any way.

Lets say your have a class with a slot fucntion

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass() {}

public slots: 
    void MySlot() { qDebug() << "RAWR";
};

So no if you want to run this synchronously in the main thread you can call that function directly. In order to connect a signal you need to create an object and connect a signal to the slot.

class MySignalClass : public QObject
{
    Q_OBJECT
public:
    MySignalClass() {}

    signalSomthign() { emit someAwesomeSignal; }

public signals: 
    void someAwesomeSignal();
};

And somwhere in the main thread you do something like

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

So now if something you can connect multiple signals to that slot object, but realisticly the code I provided won't run any different than a normal function call. You'll be able to see that with a stack trace. If you add the flag qobject::queuedConneciton to the connect then it will que the slot call in the event loop.

You can easily thread a signal as well, but this will automatically be a queuedConnection

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

Now you'll have a threaded signal basically. If your signal is going to be threaded then all you have to do is switch to signalClass.moveToThread(&someThread), when the signal is emitted signalClass will be run in the main thread.

If you don't want an object to be called, I'm not sure, lamdas might work. I've used them before but Ithink they still need to be wrapped up in a class.

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

Although I'm pretty sure with Qt5 you can even go as far as creating a slot in line within a connect. But once your using lambdas I have no idea how threads work with them. As far as I know you need an object to sit in a thread basically to force calling the slot from the main thread.

Vikkivikky answered 8/2, 2014 at 14:18 Comment(2)
I don't have a class nor an object. I also don't have signals nor slots. I just want to queue some code to the main thread queue. Or is your answer basically: "That is not possible. For every code you want to execute, you must create a new specific slot within some object which lives in the main thread." In that case, you mean that I have to create some dummy object which lives in the main thread which just sits there to execute my code?Yirinec
As far as know the only way to guarantee a call to be made in a thread with qt is first make an object to live in that thread. Even if you make a class as a wrapper to other calls Is it really that important to make sure certain calls are made in the main thread? You might be able to use a lambda to just call a function but I don't believe you can change threads without an object.Vikkivikky

© 2022 - 2024 — McMap. All rights reserved.