QT信号和槽
qt信号和槽的连接方式
qt信号和槽的连接方式分为具体分五种,在手册中查看了Qt::ConnectionType的枚举类型结果如下。
Qt::AutoConnection | 0 | (默认)如果信号的发送和接收方在相同线程的情况下,使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。在发出信号时确定连接类型。 |
---|---|---|
Qt::DirectConnection | 1 | 当发出信号时,槽函数立即被调用。槽在发送信号的线程中执行。 |
Qt::QueuedConnection | 2 | 当控制返回到接收方线程的事件循环时调用槽。槽在接收方的线程中执行。 |
Qt::BlockingQueuedConnection | 3 | 与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 触发信号后