从另一个线程发出信号是否安全?

时间:2018-09-07 09:06:17

标签: c++ multithreading qt thread-safety qt-signals

从另一个线程向对象发出信号是否安全(如果插槽以QueuedConnection连接)?我找不到特定的文档来提及这一点,我发现的最多relevant quote是这样的:

  

QObject是可重入的。它的大多数非GUI子类,例如QTimer,   QTcpSocket,QudpSocket和QProcess也可重入,从而使其   可以同时从多个线程使用这些类。   请注意,这些类旨在从以下位置创建和使用   在一个线程内; 在一个线程中创建对象并调用   不能保证其从另一个线程执行的功能

这表明可能不正确,这是否也适用于信号? QMutexLocker中有一个QMetaObject::activate,所以在我看来它可能是线程安全的...?

#include <QCoreApplication>
#include <QTimer>
#include <thread>
#include <iostream>

struct Foo : public QObject
{
    Q_OBJECT
public:
    Foo(QObject* parent) : QObject(parent) {}

public slots:
    void run()
    {
        connect(this, &Foo::signal, this, [] { std::cout << "activated"; }, Qt::QueuedConnection);

        std::thread t([this] { emit signal(); });
        if (t.joinable()) t.join();
    }

signals:
    void signal() const;
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Foo* b = new Foo(&a);
    QTimer::singleShot(0, b, &Foo::run);
    return a.exec();
}

2 个答案:

答案 0 :(得分:1)

Qt基于事件队列。每个Qt线程都有自己的队列和相关的事件循环。因此,当您遇到2个不同的对象同时存在2个不同的线程并且一个通过信号/插槽机制(通过自动或队列连接)连接到另一个对象的情况时,在发射期间会发生以下情况:信号内的代码创建了一个事件,并将其发布到对象接收器的队列中。接收者的事件循环正在队列中运行,找到发布的事件并执行适当的插槽。

保证队列是线程安全的,因此跨线程发出信号绝对安全。您问题中的引语是关于您对居住在T1T2的对象进行直接调用的情况。

有一篇很棒的文章,关于线程,q对象,信号,插槽以及所有事物如何相互关联:Threads Events QObjects。如果您想更深入地了解它,建议阅读。


关于有问题的代码。您有排队的连接,这意味着发送方和接收方都生活在一个线程中还是在另一个线程中都没有关系。发送方和接收方是2个对象还是相同的对象都没有关系。所述例程将是相同的。如果创建了自动连接,则它将具有直接呼叫,但没有。还有documentation的相关引用:

  

另一方面,您可以安全地从   QThread :: run()实现,因为信号发射是线程安全的

答案 1 :(得分:1)

是,这是首选方法,因为qt GUI阻止直接从另一个线程更改线程 因此,您应该从一个线程发出信号,并将其连接(接收)到线程束中,并根据信号类型执行一些线程代码 如果您尝试直接执行代码,则会收到此错误

QObject :: setParent:无法设置父对象,新的父对象位于另一个线程中