QT信号和槽连接方式小结

QT信号和槽

qt信号和槽的连接方式

qt信号和槽的连接方式分为具体分五种,在手册中查看了Qt::ConnectionType的枚举类型结果如下。

Qt::AutoConnection0(默认)如果信号的发送和接收方在相同线程的情况下,使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。在发出信号时确定连接类型。
Qt::DirectConnection1当发出信号时,槽函数立即被调用。槽在发送信号的线程中执行。
Qt::QueuedConnection2当控制返回到接收方线程的事件循环时调用槽。槽在接收方的线程中执行。
Qt::BlockingQueuedConnection3与Qt::QueuedConnection相同,只是发送信号的线程会阻塞,直到槽返回。如果接收方存在于发送信号的线程中,则不能使用此连接,否则应用程序将死锁。

信号和槽在同一线程下的连接测试

直接连接 DirectConnection

connect(this,&MainWindow::sig,this,&MainWindow::slot,Qt::DirectConnection);
void MainWindow::test()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用DirectConnection的方式连接
void MainWindow::slot()
{
    qDebug() << "槽函数" << "线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test 线程id 0x25c8

MainWindow::test 触发信号前

MainWindow::slot 槽函数 线程id 0x25c8

MainWindow::test 触发信号后

可以看出,采用直接连接的方式在发出信号的时候,槽函数会立即被调用。

队列连接QueuedConnection

connect(this,&MainWindow::sig,this,&MainWindow::slot,Qt::BlockingQueuedConnection);
void MainWindow::test()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用QueuedConnection的方式连接
void MainWindow::slot()
{
    qDebug() << "槽函数" << "线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test 线程id 0x3aa8

MainWindow::test 触发信号前

MainWindow::test 触发信号后

MainWindow::slot 槽函数 线程id 0x3aa8

可以看出,采用队列连接的方式触发信号时,不会马上触发,而是等当前函数执行完进入事件循环后触发。

阻塞队列连接BlockingQueuedConnection

connect(this,&MainWindow::sig,this,&MainWindow::slot,Qt::QueuedConnection);
void MainWindow::test()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用BlockingQueuedConnection的方式连接
void MainWindow::slot()
{
    qDebug() << "槽函数" << "线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test 线程id 0x848

MainWindow::test 触发信号前

QMetaObject::activate Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is MainWindow(0x7cfde0), receiver is MainWindow(0x7cfde0) (Qt:在激活BlockingQueuedConnection时检测到死锁:Sender is MainWindow(0x7cfde0), receiver is MainWindow(0x7cfde0) )

blockingQueuedConnection和QueuedConnection类似,都属于队列连接。但是blockingQueuedConnection发送信号后会阻塞当前线程,直到槽函数返回。所以当信号发送和接收为同一线程时不能使用否则会造成死锁。该连接方式可实现线程同步。

信号和槽在不同线程下的连接测试

直接连接 DirectConnection

sonthread = new SonThread();
thread = new QThread();
connect(this,&MainWindow::sig,sonthread,&SonThread::threadSlot,Qt::DirectConnection);
sonthread->moveToThread(thread);
thread->start();
test2();
void MainWindow::test2()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用DirectConnection的方式连接
void SonThread::threadSlot()
{
    qDebug() << "槽函数,子线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test2 线程id 0x35c0

MainWindow::test2 触发信号前

SonThread::threadSlot 槽函数,子线程id 0x35c0

MainWindow::test2 触发信号后

在上述例子中,sonthread对象处于子线程thread中,由于采用了DirectConnection连接,所以当sig()信号发出后,马上调用了threadslot()槽并且该槽在sig()信号发射线程中(也就是主线程)运行。这与信号的发送和接收在同一线程中的实验结果一致。但不建议在不同线程中的信号和槽采用该方式连接。仍以上面的代码举例。如果在threadslot()槽中对sonthead对象中的成员变量进行操作的同时sonthread所在的线程(thread)也对相同的成员变量进行操作。那么这种情况是不安全的。

队列连接QueuedConnection

sonthread = new SonThread();
thread = new QThread();
connect(this,&MainWindow::sig,sonthread,&SonThread::threadSlot,Qt::QueuedConnection);
sonthread->moveToThread(thread);
thread->start();
test2();
void MainWindow::test2()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用QueuedConnection的方式连接
void SonThread::threadSlot()
{
    qDebug() << "槽函数,子线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test2 线程id 0x2c38

MainWindow::test2 触发信号前

MainWindow::test2 触发信号后

SonThread::threadSlot 槽函数,子线程id 0x2d14

可以看出,采用队列连接的方式触发信号时,不会马上触发,而是等槽函数所在线程进入事件循环后触发并且槽函数在接收方线程中运行。

阻塞队列连接BlockingQueuedConnection

sonthread = new SonThread();
thread = new QThread();
connect(this,&MainWindow::sig,sonthread,&SonThread::threadSlot,Qt::QueuedConnection);
sonthread->moveToThread(thread);
thread->start();
test2();
void MainWindow::test2()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    qDebug() << "触发信号后";
}
//该信号和槽采用QueuedConnection的方式连接
void SonThread::threadSlot()
{
    qDebug() << "槽函数,子线程id" << QThread::currentThreadId();
}

输出结果:

MainWindow::test2 线程id 0xaf4

MainWindow::test2 触发信号前

SonThread::threadSlot 槽函数,子线程id 0x2adc

MainWindow::test2 触发信号后

blockingQueuedConnection和QueuedConnection类似,都属于队列连接。但是blockingQueuedConnection发送信号后会阻塞当前线程,直到槽函数返回。只有在信号的发送和接收方在不同线程中时才可使用,否则会造成死锁。使用该连接方式可以实现线程同步。

注意:

1、Qt::AutoConnection连接方式

当信号和槽在连接时未指定连接类型讲默认使用Qt::AutoConnection的连接方式。该连接方式未:如果信号的发送和接收方在相同线程的情况下,使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。特别注意的是:连接方式的确定是在信号发出时确认,而不是在连接时确认。

class Thread : public QThread
{
Q_OBJECT
signals:
    void sig( QString );
protected:
    void run() {
        qDebug() << "线程id" << QThread::currentThreadId();
        qDebug() << "信号发出前";
        emit sig("子线程发出");
        qDebug() << "信号发出后";
        this->exec();
    }
public slots:
    void slot(QString str){//主线程
        qDebug() << str <<":线程id" << QThread::currentThreadId();
    }
};
​
void MainWindow::test4()
{
    Thread * thread = new Thread();
    QObject::connect(thread, &Thread::sig, thread,&Thread::slot );
    thread->start();
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "信号发出前";
    emit thread->sig("主线程发出");
    qDebug() << "信号发出后";
}
 

MainWindow::test4 线程id 0x396c

Thread::run 线程id 0x5d0

MainWindow::test4 信号发出前

Thread::run 信号发出前

Thread::slot "主线程发出" :线程id 0x396c

Thread::run 信号发出后

MainWindow::test4 信号发出后

Thread::slot "子线程发出" :线程id 0x396c

整理得:

MainWindow::test4 线程id 0x396c

MainWindow::test4 信号发出前

Thread::slot "主线程发出" :线程id 0x396c

MainWindow::test4 信号发出后

Thread::run 线程id 0x5d0

Thread::run 信号发出前

Thread::run 信号发出后

Thread::slot "子线程发出" :线程id 0x396c

由例子可得,信号和槽连接的方式是在信号发出时确定的,在主线程test4()中发送的信号由于信号和槽在同一线程中(信号和槽都在主线程中),所以采用的是Direct的连接方式。而在run()中发送的信号与槽在不同线程中(信号发送在子线程中,槽在主线程中),所以采用的是queued的连接方式。

2、信号和槽与事件循环

当采用QueuedConnection连接方式时,槽函数的调用时机为当前线程进入事件循环。而不是等当前函数执行完就去调用(如果当前函数中调用了QCoreApplication::processEvents(QEventLoop::AllEvents);进入事件循环即可调用)。

connect(this,&MainWindow::sig,this,&MainWindow::slot,Qt::QueuedConnection);
void MainWindow::test()
{
    qDebug() << "线程id" << QThread::currentThreadId();
    qDebug() << "触发信号前";
    emit sig();
    QCoreApplication::processEvents(QEventLoop::AllEvents);
    qDebug() << "触发信号后";
}
//该信号和槽采用QueuedConnection的方式连接
void MainWindow::slot()
{
    qDebug() << "槽函数" << "线程id" << QThread::currentThreadId();
}
 

输出结果:

MainWindow::test 线程id 0x25c8

MainWindow::test 触发信号前

MainWindow::slot 槽函数 线程id 0x25c8

MainWindow::test 触发信号后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值