Rumah > pembangunan bahagian belakang > C++ > Bagaimana untuk Melaksanakan Fungsi atau Lambdas dalam Benang Diberi dalam Qt, Sama seperti GCD?

Bagaimana untuk Melaksanakan Fungsi atau Lambdas dalam Benang Diberi dalam Qt, Sama seperti GCD?

DDD
Lepaskan: 2024-12-20 04:37:13
asal
128 orang telah melayarinya

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

Bagaimana untuk melaksanakan functor atau lambda dalam urutan tertentu dalam Qt, gaya GCD?

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 */ });
Salin selepas log masuk
Salin selepas log masuk

atau:

dispatch_async(dispatch_get_main_queue(), ^{ /* do sth */ });
Salin selepas log masuk

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?

Penyelesaian

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.

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),
                         []{ ... });
Salin selepas log masuk

TL;DR untuk 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 &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)));
}
Salin selepas log masuk
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"; });
}
Salin selepas log masuk

TL; DR untuk kaedah/slot

// 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);
}
Salin selepas log masuk

Bagaimana pula dengan satu pukulan pemasa?

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 &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));
}
Salin selepas log masuk

Kod Biasa

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
Salin selepas log masuk

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()> &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
Salin selepas log masuk

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 */ });
Salin selepas log masuk
Salin selepas log masuk

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!

sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan