Dalam ObjC dengan GCD, terdapat cara untuk melaksanakan lambda dalam mana-mana utas yang memutar gelung peristiwa. Contohnya:
dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
atau:
dispatch_async(dispatch_get_main_queue(), ^{ /* do sth */ });
Ia melaksanakan sesuatu (bersamaan dengan []{ / melakukan sth / } dalam C ) dalam utas utama baris gilir, sama ada menyekat atau tidak segerak.
Bagaimana saya boleh melakukan perkara yang sama dalam Qt?
Daripada apa yang saya baca, saya rasa penyelesaiannya adalah dengan menghantar isyarat kepada beberapa objek utas utama. Tetapi objek apa? Hanya QApplication::instance()? (Itulah satu-satunya objek yang hidup dalam benang utama pada ketika itu.) Dan isyarat apakah?
Sudah tentu boleh dilakukan. Sebarang penyelesaian akan tertumpu pada penyampaian acara yang membalut functor kepada objek pengguna yang berada dalam urutan yang dikehendaki. Kami akan memanggil pengeposan metacall operasi ini. Butiran boleh dilaksanakan dalam beberapa cara.
// 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), []{ ... });
// 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))); }
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"; }); }
// 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); }
Semua kaedah di atas berfungsi daripada urutan yang tidak mempunyai gelung peristiwa. Disebabkan oleh QTBUG-66458, peruntukan berguna QTimer::singleShot memerlukan gelung peristiwa dalam urutan sumber juga. Kemudian postToObject menjadi sangat mudah, dan anda mungkin hanya menggunakan QTimer::singleShot secara langsung, walaupun ia adalah nama janggal yang menyembunyikan niat daripada mereka yang tidak biasa dengan simpulan bahasa ini. Arahan melalui fungsi yang dinamakan untuk menunjukkan maksud dengan lebih baik adalah masuk akal, walaupun anda tidak memerlukan pemeriksaan jenis:
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)); }
Mari kita tentukan masalah kita dari segi mengikuti kod biasa. Penyelesaian yang paling mudah akan menyiarkan acara sama ada pada objek aplikasi, jika utas sasaran ialah utas utama atau kepada penghantar acara untuk mana-mana urutan lain yang diberikan. Memandangkan penghantar acara akan wujud hanya selepas QThread::run dimasukkan, kami menunjukkan keperluan untuk urutan berjalan dengan mengembalikan true daripada 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
Fungsi pengeposan metacall, dalam bentuk paling mudahnya , memerlukan pengguna panggilan functor untuk menyediakan objek untuk urutan tertentu dan membuat instantiat acara panggilan functor. Pelaksanaan acara masih di hadapan kami, dan merupakan perbezaan penting antara pelbagai pelaksanaan.
#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
Untuk tujuan demonstrasi, urutan pekerja mula-mula menyiarkan metacall ke utas utama, dan kemudian menunda ke QThread ::run() untuk memulakan gelung acara untuk mendengar kemungkinan metacall daripada benang lain. Mutex digunakan untuk membolehkan pengguna thread menunggu dengan mudah untuk thread dimulakan, jika perlu oleh pelaksanaan pengguna. Penantian sedemikian diperlukan untuk pengguna acara lalai yang diberikan di atas.
dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
Atas ialah kandungan terperinci Bagaimana untuk Melaksanakan Fungsi atau Lambdas dalam Benang Diberi dalam Qt, Sama seperti GCD?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!