> 백엔드 개발 > C++ > GCD와 유사하게 Qt의 주어진 스레드에서 Functor 또는 Lambda를 실행하는 방법은 무엇입니까?

GCD와 유사하게 Qt의 주어진 스레드에서 Functor 또는 Lambda를 실행하는 방법은 무엇입니까?

DDD
풀어 주다: 2024-12-20 04:37:13
원래의
127명이 탐색했습니다.

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

Qt, GCD 스타일의 특정 스레드에서 펑터나 람다를 실행하는 방법은 무엇입니까?

ObjC with GCD에는 람다를 실행하는 방법이 있습니다. 이벤트 루프를 회전시키는 스레드 중 하나에서. 예를 들어:

dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
로그인 후 복사
로그인 후 복사

또는:

dispatch_async(dispatch_get_main_queue(), ^{ /* do sth */ });
로그인 후 복사

메인 스레드에서 무언가를 실행합니다(C의 []{ / do sth / }와 동일) 차단하거나 비동기적으로 대기열을 실행합니다.

어떻게 같은 작업을 수행할 수 있습니까? Qt?

내가 읽은 바에 따르면 메인 스레드의 일부 개체에 신호를 보내는 것이 해결책일 것 같습니다. 그런데 무슨 물건이요? 그냥 QApplication::instance()? (그 시점에서 메인 스레드에 존재하는 유일한 개체입니다.) 그리고 어떤 신호가 있습니까?

해결책

확실히 가능합니다. 모든 솔루션은 원하는 스레드에 있는 소비자 개체에 펑터를 래핑하는 이벤트를 전달하는 데 중점을 둡니다. 우리는 이 작업을 메타콜 게시라고 부르겠습니다. 세부 사항은 여러 가지 방법으로 실행될 수 있습니다.

Qt 5.10 이상 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

// 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)));
}
로그인 후 복사
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"; });
}
로그인 후 복사

TL; 메소드/슬롯에 대한 DR

// 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);
}
로그인 후 복사

싱글샷은 어떻습니까? 타이머?

위의 모든 방법은 이벤트 루프가 없는 스레드에서 작동합니다. QTBUG-66458로 인해 QTimer::singleShot을 편리하게 사용하려면 소스 스레드에도 이벤트 루프가 필요합니다. 그러면 postToObject는 매우 단순해지며 QTimer::singleShot을 직접 사용할 수도 있습니다. 비록 이 관용어에 익숙하지 않은 사람들에게 의도를 숨기는 어색한 이름이기는 하지만 말입니다. 유형 검사가 필요하지 않더라도 의도를 더 잘 나타내기 위해 명명된 함수를 통한 간접 참조는 의미가 있습니다.

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));
}
로그인 후 복사

공통 코드

문제를 다음과 같이 정의해 보겠습니다. 공통 코드를 따릅니다. 가장 간단한 솔루션은 대상 스레드가 메인 스레드인 경우 애플리케이션 객체에 이벤트를 게시하거나 다른 특정 스레드에 대한 이벤트 디스패처에 이벤트를 게시합니다. 이벤트 디스패처는 QThread::run이 입력된 후에만 존재하므로 needRunningThread에서 true를 반환하여 스레드가 실행되어야 한다는 요구 사항을 나타냅니다.

#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
로그인 후 복사

가장 간단한 형식의 메타콜 게시 함수 , 주어진 스레드에 대한 객체를 제공하기 위해 펑터 호출 소비자가 필요하고 펑터 호출 이벤트를 인스턴스화합니다. 이벤트 구현은 아직 우리보다 앞서 있으며 다양한 구현 간의 근본적인 차이점입니다.

#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
로그인 후 복사

시연을 위해 작업자 스레드는 먼저 메인 스레드에 메타콜을 게시한 다음 QThread를 따릅니다. ::run()은 다른 스레드에서 가능한 메타콜을 수신하기 위해 이벤트 루프를 시작합니다. 뮤텍스는 소비자의 구현에 필요한 경우 스레드 사용자가 스레드가 시작될 때까지 간단한 방식으로 기다릴 수 있도록 하는 데 사용됩니다. 위에 제공된 기본 이벤트 소비자에게는 이러한 대기가 필요합니다.

dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
로그인 후 복사
로그인 후 복사

위 내용은 GCD와 유사하게 Qt의 주어진 스레드에서 Functor 또는 Lambda를 실행하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿