C++ 如果目标对象死亡,Qt::BlockingQueuedConnection发射会发生什么情况?

C++ 如果目标对象死亡,Qt::BlockingQueuedConnection发射会发生什么情况?,c++,qt,conditional-statements,semaphore,signals-slots,C++,Qt,Conditional Statements,Semaphore,Signals Slots,当我使用发送方法调用时,当发送代码等待调用,但目标对象随后死亡时会发生什么情况?这会在无限的等待中结束吗?或者Qt是否会唤醒调用方并返回false(这将是一种未记录的行为,也是我自己的最佳猜测)?以下示例在invokeMethod等待BlockingQueuedConnection时删除工作对象: #include <QtCore> //a thread that can be destroyed at any time //see http://stackoverflow.com

当我使用发送方法调用时,当发送代码等待调用,但目标对象随后死亡时会发生什么情况?这会在无限的等待中结束吗?或者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"
因此,一个简短的答案是:
invokeMethod
返回
true
,但不会调用任何内容。但是,请注意,您必须保证worker对象在
invokeMethod
调用主线程(否则,它是UB)开始时仍然有效(有关更多详细信息,请参见最后一点)

下面是我通过挖掘Qt的代码得出的一系列结论:

  • ivokeMethod
    仅当传递给它的参数出现问题时(例如插槽签名与参数计数/类型不匹配、返回类型不匹配、连接类型未知等),才会返回
    false
    。看
  • 当使用
    Qt::BlockingQueuedConnection
    时,
    invokeMethod
    通过以下方式阻止调用线程。
    QSemaphore
    存储在发布到接收方对象的
    QMetaCallEvent
  • QMetaCallEvent
    被销毁时,此
    QSemaphore
  • QObject
    的析构函数负责调用被析构函数的对象。这意味着事件队列中以某个对象为目标的所有事件都将在该对象销毁时销毁。看
  • 在调用线程执行
    invokeMethod
    时,您需要确保worker对象保持活动状态,直到获得所提到的信号量,因为
    invokeMethod
    可能会在任何时候尝试访问worker对象。我认为这一要求在实践中会使事情变得复杂,因为最终可能不得不在整个
    invokeMethod
    调用中保证对象的生命周期(从而避免整个问题)
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!