为什么没有Qt连接模式在Qt :: DirectConnection和Qt :: BlockingQueuedConnection之间自动选择?

时间:2017-09-18 13:42:18

标签: multithreading qt signals-slots

以下是Qt信号/插槽连接的工作原理:

  • Direct Connection发出信号时立即调用插槽。插槽在发射器的线程中执行,该线程不一定是接收器的线程。
  • Queued Connection当控制返回到接收者线程的事件循环时,调用该槽。插槽在接收器的线程中执行。
  • Blocking Queued Connection对于排队连接调用插槽,但当前线程阻塞直到插槽返回。 注意:使用此类型连接同一线程中的对象将导致死锁。

还有一个额外的实际上是默认值:Auto Connection如果在接收对象具有亲和力的线程中发出信号,则行为与直接连接相同。否则,行为与排队连接相同。"

默认情况下,在大多数情况下效果非常好:

  • 如果在工作线程内,则通过排队连接排队执行
  • 如果在对象线程内,立即执行

但是,当从线程发出信号时,您有两种选择来处理它:"排队"或者"阻止排队"连接。

为什么没有这样的模式:

  • 如果在工作线程中,阻止排队执行
  • 如果在对象线程内,立即执行

因为,正如文档中提到的,在同一个线程中使用Blocking Queued Connection会导致死锁......所以它真的很难处理,我经常不得不创建和管理两个信号和连接在我的代码中处理:

class A
{
    Q_OBJECT
public:
    A()
    {
        connect( this, SIGNAL(changedFromThread()), this, SLOT(update()), Qt::BlockingQueuedConnection );
        connect( this, SIGNAL(changedNotFromThread()), this, SLOT(update()) );
    }

    void notifySomethingChanged()
    {
        if ( QObject().thread() != thread() )
            emit changedFromThread(); // would dead-lock if in same thread
        else
            emit changedNotFromThread();
    } 

public slots:
    void update()
    {
        // Do some changes to A that cannot be done from a worker thread
    }

signals:
    void changedFromThread();
    void changedNotFromThread();
};

如果有这种模式(让我们称之为Qt::AutoBlockingConnection),我本可以写一下:

class A
{
    Q_OBJECT
public:
    A()
    {
        connect( this, SIGNAL(changedFromThread()), this, SLOT(update()), Qt::AutoBlockingConnection );        
    }

    void notifySomethingChanged()
    {
        emit changedFromThread(); // would dead-lock if in same thread
    } 

public slots:
    void update()
    {
        // Do some changes to A that cannot be done from a worker thread
    }

signals:
    void changedFromThread();
};

为什么只提供线程友好连接以在Qt::DirectConnectionQt::QueuedConnection之间进行交换,但是在Qt::DirectConnectionQt::BlockingQueuedConnection之间无交换,是否有任何正当理由?

1 个答案:

答案 0 :(得分:0)

我猜想Qt开发团队根本不希望您指定连接类型。 我可以理解这种设计,从某种意义上说,几乎应该避免故意阻止,如果要阻止UI线程,则更应避免。

如果您希望同步生活在不同线程中的2 QObject,请在它们两个中使用信号和插槽,并进行相应连接。

但是,如果您处于主从场景(通常QMainWindowQObject一起拥有QObject::moveToThread(new QThread)),则可以:

  • 听从QObject到您的QMainWindow的信号
  • 使用QObject呼叫QMetaObject::invokeMethod插槽 QMainWindow,因此方法调用由异步处理 QObject线程。