gpt4 book ai didi

c++ - 如果目标对象死亡,Qt::BlockingQueuedConnection emission 会发生什么?

转载 作者:太空狗 更新时间:2023-10-29 23:11:10 30 4
gpt4 key购买 nike

当我使用 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 - 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!

所以一个简短的回答是:invokeMethod 返回 true 但没有任何调用。但是,请注意,您必须保证 worker 对象在 开始 时仍然有效(有关详细信息,请参阅最后一点) invokeMethod 调用主线程(否则,它是 UB)。

以下是我通过深入研究 Qt 代码得出的结论列表:

  • ivokeMethod 仅在传递给它的参数存在问题时返回 false(例如,插槽签名与参数计数/类型不匹配、返回类型不匹配、未知连接类型, ...)。参见 here .
  • 当使用 Qt::BlockingQueuedConnection 时,invokeMethod 通过 acquiring a QSemaphore 阻塞调用线程. QSemaphore 存储在发送到接收者对象的 QMetaCallEvent 中。
  • 这个QSemaphore is releasedQMetaCallEvent 被销毁时。
  • QObject的析构函数负责调用QCoreApplication::removePostedEvents()对于被破坏的对象。这意味着事件队列中针对某个对象的所有事件都会在该对象销毁时被销毁。参见 here .
  • 您需要确保在调用线程执行 invokeMethod 时 worker 对象保持事件状态,直到获取提到的信号量,因为 invokeMethod 可能会尝试访问 worker 对象在任何时候。我认为这一要求在实践中会使事情变得复杂,因为最终可能必须在整个 invokeMethod 调用期间保证对象的生命周期(从而避免这整个问题)。

关于c++ - 如果目标对象死亡,Qt::BlockingQueuedConnection emission 会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51447032/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com