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()? (그 시점에서 메인 스레드에 존재하는 유일한 개체입니다.) 그리고 어떤 신호가 있습니까?
확실히 가능합니다. 모든 솔루션은 원하는 스레드에 있는 소비자 개체에 펑터를 래핑하는 이벤트를 전달하는 데 중점을 둡니다. 우리는 이 작업을 메타콜 게시라고 부르겠습니다. 세부 사항은 여러 가지 방법으로 실행될 수 있습니다.
// 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); }
위의 모든 방법은 이벤트 루프가 없는 스레드에서 작동합니다. QTBUG-66458로 인해 QTimer::singleShot을 편리하게 사용하려면 소스 스레드에도 이벤트 루프가 필요합니다. 그러면 postToObject는 매우 단순해지며 QTimer::singleShot을 직접 사용할 수도 있습니다. 비록 이 관용어에 익숙하지 않은 사람들에게 의도를 숨기는 어색한 이름이기는 하지만 말입니다. 유형 검사가 필요하지 않더라도 의도를 더 잘 나타내기 위해 명명된 함수를 통한 간접 참조는 의미가 있습니다.
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)); }
문제를 다음과 같이 정의해 보겠습니다. 공통 코드를 따릅니다. 가장 간단한 솔루션은 대상 스레드가 메인 스레드인 경우 애플리케이션 객체에 이벤트를 게시하거나 다른 특정 스레드에 대한 이벤트 디스패처에 이벤트를 게시합니다. 이벤트 디스패처는 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()> & 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
시연을 위해 작업자 스레드는 먼저 메인 스레드에 메타콜을 게시한 다음 QThread를 따릅니다. ::run()은 다른 스레드에서 가능한 메타콜을 수신하기 위해 이벤트 루프를 시작합니다. 뮤텍스는 소비자의 구현에 필요한 경우 스레드 사용자가 스레드가 시작될 때까지 간단한 방식으로 기다릴 수 있도록 하는 데 사용됩니다. 위에 제공된 기본 이벤트 소비자에게는 이러한 대기가 필요합니다.
dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
위 내용은 GCD와 유사하게 Qt의 주어진 스레드에서 Functor 또는 Lambda를 실행하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!