Bagaimana untuk melaksanakan functor atau lambda dalam urutan tertentu dalam Qt, gaya GCD?
Masalah:
Dalam ObjC dengan GCD, anda boleh melaksanakan lambda dalam mana-mana benang yang memutar gelung peristiwa menggunakan fungsi dispatch_sync atau dispatch_async. Ia melaksanakan sesuatu (bersamaan dengan [] { /* do sth */ } dalam C ) dalam baris gilir benang utama, sama ada menyekat atau tidak segerak. Bagaimanakah anda boleh melakukan perkara yang sama dalam Qt?
Penyelesaian:
Dalam Qt, anda boleh mencapai gelagat yang serupa dengan menyampaikan peristiwa yang membungkus functor kepada objek pengguna berada dalam urutan yang dikehendaki, satu proses yang dikenali sebagai penyiaran metacall. Begini cara anda boleh melakukannya:
Qt 5.10 ke atas 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 untuk functors Qt 5.10 ke atas
// 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)); }
TL;DR untuk kaedah/slot Qt 5.10 ke atas
// 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)); }
Qt 5.10 ke atas TL;DR: Bagaimana pula dengan pemasa pukulan tunggal?
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)); }
Kod Biasa Qt 5.10 & up
#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
Penyelesaian Qt 4/5 Menggunakan Objek Sementara Sebagai Sumber Isyarat
#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
Menggunakan Penyelesaian Qt 4/5 QEvent Destructor
#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; } };
Penyelesaian Qt 5 Menggunakan QMetaCallEvent Persendirian
#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*))) {} };
Penyelesaian Qt 4/5 Menggunakan Acara Tersuai dan Pengguna
#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; } };
Atas ialah kandungan terperinci Bagaimana untuk Melaksanakan Fungsi atau Lambdas dalam Benang Qt Tertentu?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!