下面的示例将在
invokeMethod
正在等待
BlockingQueuedConnection
:
#include <QtCore>
//a thread that can be destroyed at any time
//see http://stackoverflow.com/a/25230470
class SafeThread : public QThread{
using QThread::run;
public:
explicit SafeThread(QObject* parent= nullptr):QThread(parent){}
~SafeThread(){ quit(); wait(); }
};
//The function queues a functor to get executed in a specified worker's thread
template <typename Func>
void PostToThread(QThread* thread, Func&& f) {
//see http://stackoverflow.com/a/21653558
QObject temporaryObject;
QObject::connect(&temporaryObject, &QObject::destroyed,
thread->eventDispatcher(), std::forward<Func>(f),
Qt::QueuedConnection);
}
//a typical QObject worker that can "printName"
class Worker : public QObject {
Q_OBJECT
public:
using QObject::QObject;
~Worker() {
qInfo() << "destroying " << objectName()
<< " in " << QThread::currentThread()->objectName();
}
Q_SLOT void printName() {
qInfo() << "my name is " << objectName()
<< " in " << QThread::currentThread()->objectName();
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
//create worker
Worker *worker = new Worker;
worker->setObjectName("worker");
//start worker thread and move worker to it
SafeThread t;
worker->moveToThread(&t);
t.start();
//set thread names (for better output)
QThread::currentThread()->setObjectName("main_thread");
t.setObjectName("worker_thread");
//normal QMetaObject::invokeMethod usage
if(QMetaObject::invokeMethod(worker, "printName",
Qt::BlockingQueuedConnection)) {
qInfo() << "printName called successfully before deletion";
}
//the lambda function will be executed in the worker thread
PostToThread(&t, [worker]{
qInfo() << "blocking " << QThread::currentThread()->objectName();
QThread::sleep(2); //block worker thread for 2 seconds
delete worker; //delete worker
});
//at this point the worker thread is about to destroy the worker object (but
//hasn't done so yet)
if(QMetaObject::invokeMethod(worker, "printName",
Qt::BlockingQueuedConnection)) {
qInfo() << "printName called successfully after deletion!";
}
QTimer::singleShot(100, &a, &QCoreApplication::quit);
return a.exec();
}
#include "main.moc"
输出(在Qt 5.9.1、Qt 5.7-Windows、Debian上测试):
my name is "worker" in "worker_thread"
printName called successfully before deletion
blocking "worker_thread"
destroying "worker" in "worker_thread"
printName called successfully after deletion!
所以简单的答案是:
调用方法
收益率
true
但什么都没有。但是,请注意,您必须保证worker对象仍然有效。
在
(详见最后一点)
调用方法
调用主线程(否则,它是ub)。
下面是我通过挖掘qt代码得出的结论列表:
-
ivokeMethod
收益率
false
仅当传递给它的参数有问题时(例如槽签名与参数计数/类型不匹配、返回类型不匹配、未知连接类型…)。见
here
.
-
使用时
Qt::BlockingQueuedConnection
,
调用方法
通过阻止调用线程
acquiring a
QSemaphore
. 这个
Q信号机
存储到
QMetaCallEvent
发布到接收对象。
-
这个
Q信号机
is released
当
QmetaCallEvent(详细事件)
被摧毁。
-
QObject
的析构函数负责调用
QCoreApplication::removePostedEvents()
对于被破坏的对象。这意味着事件队列中针对某个对象的所有事件在该对象被销毁时都会被销毁。见
here
.
-
您需要确保在调用线程执行时工作对象保持活动状态
调用方法
直到获得上述信号量,因为
调用方法
可能会尝试在任何点访问工作对象。我认为这一要求在实践中会使事情变得复杂,因为人们最终可能不得不保证整个对象的生命周期。
调用方法
打电话(因此避免了整个问题)。