如果目标对象死亡,Qt::BlockingQueuedConnection发射会发生什么
What happens with Qt::BlockingQueuedConnection emission if target object dies?
当我使用invokeMethod
发送方法调用时,当发送代码等待调用,但目标对象随后死亡时,会发生什么?这会在无限的等待中结束吗?或者Qt会唤醒调用者并返回false
(这将是一种未记录的行为,也是我自己的最佳猜测(?
以下示例在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-窗口、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!
因此,一个简短的答案是:invokeMethod
返回true
,但没有调用任何内容。但是,请注意,您必须保证工作对象在开始时仍然有效(有关更多详细信息,请参阅最后一点(invokeMethod
调用主线程(否则,它是UB(。
以下是我通过挖掘Qt的代码得出的结论列表:
ivokeMethod
只有在传递给它的参数出现问题时才返回false
(例如,插槽签名与参数计数/类型不匹配、返回类型不匹配或连接类型未知等(。请参阅此处- 当使用
Qt::BlockingQueuedConnection
时,invokeMethod
通过获取QSemaphore
来阻塞调用线程。CCD_ 13被存储到被张贴到接收器对象的CCD_ - 当
QMetaCallEvent
被破坏时,该QSemaphore
被释放 QObject
的析构函数负责调用被析构函数对象的QCoreApplication::removePostedEvents()
。这意味着事件队列中以某个对象为目标的所有事件都将在该对象销毁时销毁。请参见此处- 您需要确保工作对象在调用线程执行
invokeMethod
时保持活动状态,直到获得上述信号量,因为invokeMethod
可能会在任何时候尝试访问工作对象。我认为这个要求在实践中可能会使事情变得复杂,因为最终可能需要在整个invokeMethod
调用中保证对象的生存期(从而避免整个问题(
相关文章:
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 警告处理为错误这里有什么问题
- 什么时候调用组成单元对象的析构函数
- #定义c-预处理器常量..我做错了什么
- 努力将整数转换为链表。不知道我在这里做错了什么
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 什么时候在C++中返回常量引用是个好主意
- 当在同一名称空间中有两个具有相同签名的函数时,会发生什么
- C++避免重复声明的语法是什么
- c++库的公共头文件中应该包含什么
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- ifstream什么都没读
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 我应该使用什么来代替void作为变体中的替代类型之一
- 如果目标对象死亡,Qt::BlockingQueuedConnection发射会发生什么