C++ 如何在Qt、GCD风格的给定线程中执行函子或lambda?
在带有GCD的ObjC中,有一种方法可以在任何旋转事件循环的线程中执行lambda。例如:C++ 如何在Qt、GCD风格的给定线程中执行函子或lambda?,c++,multithreading,qt,C++,Multithreading,Qt,在带有GCD的ObjC中,有一种方法可以在任何旋转事件循环的线程中执行lambda。例如: dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ }); 或: 它在主线程的队列中执行一些东西(相当于C++中的[]{/*dosth*/}),可以是阻塞的,也可以是异步的 如何在Qt中执行相同的操作 从我所读到的内容来看,我猜解决方案是以某种方式向主线程的某个对象发送一个信号。但是什么目标呢?只是QApplication::instance
dispatch_sync(dispatch_get_main_queue(), ^{ /* do sth */ });
或:
它在主线程的队列中执行一些东西(相当于C++中的[]{/*dosth*/}
),可以是阻塞的,也可以是异步的
如何在Qt中执行相同的操作
从我所读到的内容来看,我猜解决方案是以某种方式向主线程的某个对象发送一个信号。但是什么目标呢?只是QApplication::instance()
?(这是在该点上主线程中唯一存在的对象。)以及什么信号
从目前的答案和我目前的研究来看,似乎我真的需要一些虚拟对象来放置在主线程中,其中包含一些插槽,这些插槽只是等待输入一些代码来执行 因此,我决定子类
QApplication
来添加它。我当前的代码不起作用(但也许您可以帮助):
#包括
#包括
#包括
#包括
#包括
类应用程序:公共QApplication
{
Q_对象
公众:
App();
信号:
公众时段:
void genericExec(std::function func){
func();
}
私人:
//缓存这个
QMetaMethod genericExec_方法;
公众:
void invokeGenericExec(std::function func,Qt::ConnectionType connType){
if(!genericExec_方法){
QByteArray normalizedSignature=QMetaObject::normalizedSignature(“genericExec(std::function)”;
int methodIndex=this->metaObject()->indexOfSlot(normalizedSignature);
断言(methodIndex>=0);
genericExec_method=this->metaObject()->方法(methodIndex);
}
invoke(this,connType,Q_参数(std::function,func));
}
};
内联函数
void execInMainThread_sync(std::function func){
如果(qApp->thread()==QThread::currentThread())
func();
否则{
((App*)qApp)->invokeGenericExec(func,Qt::BlockingQueuedConnection);
}
}
内联函数
void execInMainThread_async(std::function func){
((App*)qApp)->invokeGenericExec(func,Qt::QueuedConnection);
}
我完全不知道你在说什么,但我会尽力回答你的问题
假设你有一个有吃角子老虎功能的班级
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass() {}
public slots:
void MySlot() { qDebug() << "RAWR";
};
在主线程中,您可以执行以下操作
MyClass slotClass;
MySignalClass signalClass;
qobject::connect(&signalClass, SIGNAL(someAwesomeSignal), &slotClass(), SLOT(MySlot)));
现在,如果你可以将多个信号连接到插槽对象,但实际上我提供的代码不会运行任何与普通函数调用不同的东西。您可以通过堆栈跟踪看到这一点。如果将标志qobject::QueuedConnection添加到connect,则它将在事件循环中取消插槽调用
您也可以轻松地对信号执行线程,但这将自动成为queuedConnection
MyClass slotClass;
MySignalClass signalClass;
QThread someThread;
slotClass.moveToThread(&someThread);
qobject::connect(&signalClass, SIGNAL(someAwesomeSignal), &slotClass(), SLOT(MySlot)));
现在基本上有一个线程信号。如果您的信号将被线程化,那么您所要做的就是切换到signalClass.moveToThread(&someThread),当信号发出时,signalClass将在主线程中运行
如果您不想调用对象,我不确定lamdas是否可以工作。我以前用过它们,但我认为它们仍然需要在课堂上进行包装
qobject::connect(&signalClass, &slotClass::MySlot, [=]() { /* whatever */ });
虽然我很确定使用Qt5,您甚至可以在connect中创建一个插槽。但是一旦你使用了lambdas,我就不知道线程是如何使用它们的了。据我所知,线程中需要一个对象来强制从主线程调用插槽。这当然是可能的。任何解决方案都将以传递事件为中心,该事件将函子包装到驻留在所需线程中的使用者对象。我们将此操作称为“全部过帐”。这些细节可以通过几种方式执行 Qt 5.10及以上TL;博士 TL;函子的DR 通用代码 让我们用以下通用代码来定义我们的问题。最简单的解决方案将事件发布到应用程序对象(如果目标线程是主线程)或任何其他给定线程的事件调度器。由于事件调度程序仅在输入
QThread::run
后才存在,因此我们通过从needsRunningThread
返回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
最后,我们启动上面的工作线程,将元调用发布到主(应用程序)线程,应用程序线程将元调用发布到工作线程
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.thread()->setObjectName("main");
Worker worker;
worker.setObjectName("worker");
qDebug() << "worker thread:" << &worker;
qDebug() << "main thread:" << QThread::currentThread();
if (FunctorCallConsumer::needsRunningThread()) {
worker.start();
worker.waitForStart();
}
postMetaCall(&worker, []{ qDebug() << "main functor executes in thread" << QThread::currentThread(); });
if (!FunctorCallConsumer::needsRunningThread()) worker.start();
QMetaObject::invokeMethod(&a, "quit", Qt::QueuedConnection);
return a.exec();
}
使用临时对象作为信号源的Qt 5解决方案
qt5最简单的方法是使用一个临时的QObject
作为信号源,并将函子连接到它的destrocted(QObject*)
信号。当postMetaCall
返回时,signalSource
被破坏,发出其destrocted
信号,并将元调用发布到代理对象
这可能是C++11风格中最简洁、最直接的实现。signalSource
对象以C++11 RAII方式使用,以消除其破坏的副作用。“副作用”一词在C++11的语义中有一定的含义,不应该被解释为“不可靠”或“不受欢迎”——这是绝对错误的QObject
与我们签订的合同是在其析构函数执行过程中的某个时间发出已销毁的。我们非常欢迎使用这个事实
#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
// Common Code follows here
只发布到主线程,事情变得更加简单:
class FunctorCallEvent : public QEvent {
std::function<void()> m_fun;
public:
FunctorCallEvent(const std::function<void()> & fun) :
QEvent(QEvent::None), m_fun(fun) {}
FunctorCallEvent(std::function<void()> && fun, QObject * receiver) :
QEvent(QEvent::None), m_fun(std::move(fun)) {}
~FunctorCallEvent() {
m_fun();
}
};
void postToMainThread(const std::function<void()> & fun) {
QCoreApplication::postEvent(qApp, new FunctorCallEvent(fun);
}
void postToMainThread(std::function<void()> && fun) {
QCoreApplication::postEvent(qApp, new FunctorCallEvent(std::move(fun)));
}
使用自定义事件和使用者的Qt 4/5解决方案
我们重新实现对象的event()
方法,并让它调用函子。这将在函子发布到的每个线程中调用显式事件使用者对象。当对象的线程完成时,或者对于主线程,当应用程序实例被销毁时,对象将被清除。它在Qt4和Qt5上都起作用。使用右值引用可以避免复制临时函子
#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;
}
FunctorCallConsumer() {}
~FunctorCallConsumer() {
qDebug() << "consumer done for thread" << thread();
Q_ASSERT(thread());
QMutexLocker lock(&m_threadObjectMutex);
m_threadObjects.remove(thread());
}
static void deleteAppThreadObject() {
delete m_appThreadObject;
m_appThreadObject = nullptr;
}
public:
static bool needsRunningThread() { return false; }
static FunctorCallConsumer * forThread(QThread * thread) {
QMutexLocker lock(&m_threadObjectMutex);
Map map = m_threadObjects;
lock.unlock();
Map::const_iterator it = map.find(thread);
if (it != map.end()) return *it;
FunctorCallConsumer * consumer = new FunctorCallConsumer;
consumer->moveToThread(thread);
if (thread != qApp->thread())
QObject::connect(thread, SIGNAL(finished()), consumer, SLOT(deleteLater()));
lock.relock();
it = m_threadObjects.find(thread);
if (it == m_threadObjects.end()) {
if (thread == qApp->thread()) {
Q_ASSERT(! m_appThreadObject);
m_appThreadObject = consumer;
qAddPostRoutine(&deleteAppThreadObject);
}
m_threadObjects.insert(thread, consumer);
return consumer;
} else {
delete consumer;
return *it;
}
}
};
QObject * FunctorCallConsumer::m_appThreadObject = nullptr;
QMutex FunctorCallConsumer::m_threadObjectMutex;
FunctorCallConsumer::Map FunctorCallConsumer::m_threadObjects;
// Common Code follows here
#包括
#包括
类FunctorCallEvent:公共QEvent{
std::函数m_fun;
公众:
FunctorCallEvent(常量s
// Qt 5 - alternative version
template <typename F>
static void postToObject2(F && fun, QObject * obj = qApp) {
if (qobject_cast<QThread*>(obj))
qWarning() << "posting a call to a thread object - consider using postToThread";
QObject src;
QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun),
Qt::QueuedConnection);
}
template <typename F>
static void postToThread2(F && fun, QThread * thread = qApp->thread()) {
QObject * obj = QAbstractEventDispatcher::instance(thread);
Q_ASSERT(obj);
QObject src;
QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun),
Qt::QueuedConnection);
}
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);
}
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));
}
#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
class Worker : public QThread {
QMutex m_started;
void run() {
m_started.unlock();
postMetaCall(qApp->thread(), []{
qDebug() << "worker functor executes in thread" << QThread::currentThread();
});
QThread::run();
}
public:
Worker(QObject * parent = 0) : QThread(parent) { m_started.lock(); }
~Worker() { quit(); wait(); }
void waitForStart() { m_started.lock(); m_started.unlock(); }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.thread()->setObjectName("main");
Worker worker;
worker.setObjectName("worker");
qDebug() << "worker thread:" << &worker;
qDebug() << "main thread:" << QThread::currentThread();
if (FunctorCallConsumer::needsRunningThread()) {
worker.start();
worker.waitForStart();
}
postMetaCall(&worker, []{ qDebug() << "main functor executes in thread" << QThread::currentThread(); });
if (!FunctorCallConsumer::needsRunningThread()) worker.start();
QMetaObject::invokeMethod(&a, "quit", Qt::QueuedConnection);
return a.exec();
}
worker thread: QThread(0x7fff5692fc20, name = "worker")
main thread: QThread(0x7f86abc02f00, name = "main")
main functor executes in thread QThread(0x7fff5692fc20, name = "worker")
worker functor executes in thread QThread(0x7f86abc02f00, name = "main")
#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
// Common Code follows here
void postToMainThread(const std::function<void()> & fun) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, qApp, [=](QObject*){
fun();
});
}
#ifdef __cpp_init_captures
void postToMainThread(std::function<void()> && fun) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, qApp, [fun(std::move(fun))](QObject*){
fun();
});
}
#endif
#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;
}
};
// Common Code follows here
class FunctorCallEvent : public QEvent {
std::function<void()> m_fun;
public:
FunctorCallEvent(const std::function<void()> & fun) :
QEvent(QEvent::None), m_fun(fun) {}
FunctorCallEvent(std::function<void()> && fun, QObject * receiver) :
QEvent(QEvent::None), m_fun(std::move(fun)) {}
~FunctorCallEvent() {
m_fun();
}
};
void postToMainThread(const std::function<void()> & fun) {
QCoreApplication::postEvent(qApp, new FunctorCallEvent(fun);
}
void postToMainThread(std::function<void()> && fun) {
QCoreApplication::postEvent(qApp, new FunctorCallEvent(std::move(fun)));
}
#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*))) {}
// Metacalls with slot objects require an argument array for the return type, even if it's void.
};
// Common Code follows here
#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;
}
FunctorCallConsumer() {}
~FunctorCallConsumer() {
qDebug() << "consumer done for thread" << thread();
Q_ASSERT(thread());
QMutexLocker lock(&m_threadObjectMutex);
m_threadObjects.remove(thread());
}
static void deleteAppThreadObject() {
delete m_appThreadObject;
m_appThreadObject = nullptr;
}
public:
static bool needsRunningThread() { return false; }
static FunctorCallConsumer * forThread(QThread * thread) {
QMutexLocker lock(&m_threadObjectMutex);
Map map = m_threadObjects;
lock.unlock();
Map::const_iterator it = map.find(thread);
if (it != map.end()) return *it;
FunctorCallConsumer * consumer = new FunctorCallConsumer;
consumer->moveToThread(thread);
if (thread != qApp->thread())
QObject::connect(thread, SIGNAL(finished()), consumer, SLOT(deleteLater()));
lock.relock();
it = m_threadObjects.find(thread);
if (it == m_threadObjects.end()) {
if (thread == qApp->thread()) {
Q_ASSERT(! m_appThreadObject);
m_appThreadObject = consumer;
qAddPostRoutine(&deleteAppThreadObject);
}
m_threadObjects.insert(thread, consumer);
return consumer;
} else {
delete consumer;
return *it;
}
}
};
QObject * FunctorCallConsumer::m_appThreadObject = nullptr;
QMutex FunctorCallConsumer::m_threadObjectMutex;
FunctorCallConsumer::Map FunctorCallConsumer::m_threadObjects;
// Common Code follows here
template <typename Func>
inline static void MyRunLater(Func func) {
QTimer *t = new QTimer();
t->moveToThread(qApp->thread());
t->setSingleShot(true);
QObject::connect(t, &QTimer::timeout, [=]() {
func();
t->deleteLater();
});
QMetaObject::invokeMethod(t, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}
void QTimer::singleShot(int msec, const QObject *context, Functor functor)
QTimer::singleShot(0, qApp, []()
{
qDebug() << "hi from event loop";
});
QEventLoop loop;
Q_UNUSED(loop)
QTimer::singleShot(0, qApp, []()
{
qDebug() << "singleShot from std thread";
});
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include <thread>
#include <QThread>
#include <QEventLoop>
#include <QThread>
using std::thread;
class TestObj
:public QObject
{
// Used new connect syntax no need for Q_OBJECT define
// you SHOULD use it. I used just to upload one file
//Q_OBJECT
public slots:
void doWork()
{
qDebug() << "QThread id" << QThread::currentThreadId();
QTimer::singleShot(0, qApp, []()
{
qDebug() << "singleShot from QThread" << QThread::currentThreadId();
});
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id" << QThread::currentThreadId();
thread testThread([]()
{
QEventLoop loop;
Q_UNUSED(loop)
qDebug() << "std::thread id" << QThread::currentThreadId();
QTimer::singleShot(0, qApp, []()
{
qDebug() << "singleShot from std thread" << QThread::currentThreadId();
});
qDebug() << "std::thread finished";
});
testThread.detach();
QThread testQThread;
TestObj testObj;
testObj.moveToThread(&testQThread);
QObject::connect(&testQThread, &QThread::started, &testObj, &TestObj::doWork);
testQThread.start();
return a.exec();
}
QMetaObject::invokeMethod(socketManager,"newSocket",
Qt::QueuedConnection,
Q_ARG(QString, host),
Q_ARG(quint16, port.toUShort()),
Q_ARG(QString, username),
Q_ARG(QString, passhash)
);
QMetaObject::invokeMethod(socketManager,[=](){
socketManager->newSocket(host,port.toUShort(),username,passhash);
},Qt::QueuedConnection);