简体   繁体   English

在传递的对象被销毁之前发送信号传递QObject指针作为参数是否安全?

[英]Is it safe to emit signal passing QObject pointer as parameter right before the passed object is going to be destroyed?

Lets consider this simple example: 让我们考虑这个简单的例子:

Class Emitter: public QObject {
...
signal:
    surfaceDestroyed(QObject*);
public:
    void emittingMethod(QObject* surface) {
        emit surfaceDestroyed(surface);
        delete surface;
    }
}

I have a queued connection for this case 我有这种情况的排队连接

connect(emitterObject, SIGNAL(surfaceDestroyed(QObject*), receiverObject,
    SLOT(onSurfaceDestroyed(QObject*)), Qt::QueuedConnection);

In onSurfaceDestroyed method the received QObject is dereferenced and used 在onSurfaceDestroyed方法中,取消引用并使用接收到的QObject

So the question is how safe this code is? 那么问题是这段代码的安全性如何? I've read pretty much info on that on QT site and here, but I still don't have a clear understanding of the problem. 我在QT网站上已经阅读了很多相关信息,但我仍然没有清楚地了解这个问题。

In my opinion this code is not safe, as a signal can be sent, then surface destroyed, than when an event comes for receiverObject once the event loop is processed and receiver object accesses released memory, thus SIGSEGV 在我看来,这个代码是不安全的,因为一旦处理了事件循环并且接收器对象访问释放的内存,就会发送信号,然后表面被破坏,而不是事件发生在receiverObject上,因此SIGSEGV

This is a simplified example of real code, so it's hard to track if crashes can happen because of that. 这是真实代码的简化示例,因此很难跟踪崩溃是否会因此而发生。

It depends on the type of the signal-slot connection. 这取决于信号槽连接的类型。 Connections are direct by default if the sender and the receiver belong to the same thread. 如果发送方和接收方属于同一个线程,则默认情况下连接是直接的。 In this case the slot will be called immediately when you emit the signal. 在这种情况下,当您发出信号时,将立即调用插槽。 When the signal function is finished, you can be sure that the slot has also finished. 信号功能完成后,您可以确定插槽已完成。

When the sender and the receiver belong to different threads, the connection is queued by default. 当发送方和接收方属于不同的线程时,默认情况下连接排队 In this case your code is not safe. 在这种情况下,您的代码不安全。 The slot can be called much later than the signal was called. 可以在调用信号之前调用插槽。 Even deleteLater will not save the situation because it's processed by the sender's thread's event loop and doesn't depend on the other thread's event loop. 即使deleteLater也不会保存这种情况,因为它由发送者的线程的事件循环处理,并且不依赖于其他线程的事件循环。

So if you want to write such code, ensure that your objects are in the same thread. 因此,如果要编写此类代码,请确保对象位于同一个线程中。 You can pass Qt::DirectConnection option to connect function to make connection type more clear. 您可以通过Qt::DirectConnection选项connect函数以使连接类型更清晰。

If you want to use queued connection, you can emit eg aboutToBeDeleted signal in the sender. 如果要使用排队连接,可以在发送aboutToBeDeleted发出例如aboutToBeDeleted信号。 The receiver will receive that signal, process it somehow and respond with cleanUpCompleted signal that will trigger the actual object deletion. 接收器将接收该信号,以某种方式处理它并使用cleanUpCompleted信号进行响应, cleanUpCompleted信号将触发实际的对象删除。

Also consider using standard QObject::destroyed signal. 还要考虑使用标准的QObject::destroyed信号。 It's called immediately before the object is destroyed but can be useful in many cases. 它在对象被销毁之前立即调用,但在许多情况下可能很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM