QT信号和槽机制总结
QT信号和槽连接方式
| 连接方式 | 作用 |
|---|---|
| Qt::AutoConnection | (缺省值,默认值)如果信号的接收者与发射者在统一线程,就使用Qt::DirectConnection方式,否则使用 Qt::QueuedConnection方式,在信号发射时自动确定关联方式 |
| Qt:DirectConnection | 信号被发射时槽函数立即执行,槽函数与信号在同一个线程 |
| Qt::QueuedConnection | 在事件循环回到接受者线程后执行槽函数,槽函数与信号在不同的线程 |
| Qt::BlockingQueuedConnection | 与Qt::QueuedConnection相似,只是信号线程会阻塞直到槽函数执行完毕,当信号与槽函数在同一个线程时绝不能使用这种方式,否则会造成死锁。 |
信号和槽在同一线程下的连接测试
1.直接连接 DirectConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
connect(this, SIGNAL(sig()), this,SLOT(on_do()),Qt::DirectConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
void SignalSoltConnectionTypeDemo::on_do()
{
qDebug() << QStringLiteral("槽函数的线程Id:") << QThread::currentThread();
}
输出信息如下:
"线程Id:" QThread(0x1ea446a7340)
"触发信号前"
"槽函数的线程Id:" QThread(0x1ea446a7340)
"触发信号后"
可以看出,采用直接连接的方式在发出信号的时候,槽函数会立即被调用
2.队列连接QueuedConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
connect(this, SIGNAL(sig()), this,SLOT(on_do()),Qt::QueuedConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
void SignalSoltConnectionTypeDemo::on_do()
{
qDebug() << QStringLiteral("槽函数的线程Id:") << QThread::currentThread();
}
输出结果如下:
"线程Id:" QThread(0x25ae7d68680)
"触发信号前"
"触发信号后"
"槽函数的线程Id:" QThread(0x25ae7d68680)
可以看出,采用队列连接的方式触发信号时,不会马上触发,而是等当前函数执行完进入事件循环后触发。
3.阻塞队列连接BlockingQueuedConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
connect(this, SIGNAL(sig()), this,SLOT(on_do()),Qt::BlockingQueuedConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
void SignalSoltConnectionTypeDemo::on_do()
{
qDebug() << QStringLiteral("槽函数的线程Id:") << QThread::currentThread();
}
输出结果如下:
"线程Id:" QThread(0x25703427d40)
"触发信号前"
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is SignalSoltConnectionTypeDemo(0xe59318f7e8), receiver is SignalSoltConnectionTypeDemo(0xe59318f7e8)
blockingQueuedConnection和QueuedConnection类似,都属于队列连接。但是blockingQueuedConnection发送信号后会阻塞当前线程,直到槽函数返回。所以当信号发送和接收为同一线程时不能使用否则会造成死锁。该连接方式可实现线程同步。
信号和槽在不同线程下的连接测试
1.直接连接 DirectConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
MyThread* subThread = new MyThread(this);
subThread->start();
connect(this, SIGNAL(sig()), subThread, SLOT(on_do()), Qt::DirectConnection);
//connect(this, &SignalSoltConnectionTypeDemo::sig, subThread,&MyThread::on_do, Qt::QueuedConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
输出结果如下:
"线程Id:" QThread(0x1e8cd677c00)
"触发信号前"
"子线程Id:" QThread(0x1e8cd677c00)
"触发信号后"
在上述例子中,sonthread对象处于子线程thread中,由于采用了DirectConnection连接,所以当sig()信号发出后,马上调用了threadslot()槽并且该槽在sig()信号发射线程中(也就是主线程)运行。这与信号的发送和接收在同一线程中的实验结果一致。但不建议在不同线程中的信号和槽采用该方式连接。仍以上面的代码举例。如果在threadslot()槽中对sonthead对象中的成员变量进行操作的同时sonthread所在的线程(thread)也对相同的成员变量进行操作。那么这种情况是不安全的。
2.队列连接QueuedConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
MyThread* subThread = new MyThread(this);
subThread->start();
connect(this, SIGNAL(sig()), subThread, SLOT(on_do()), Qt::QueuedConnection);
//connect(this, &SignalSoltConnectionTypeDemo::sig, subThread,&MyThread::on_do, Qt::QueuedConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
输出信息如下:
"线程Id:" QThread(0x1b697ee7ac0)
"触发信号前"
"触发信号后"
"子线程Id:" QThread(0x1b697ee7ac0)
可以看出,采用队列连接的方式触发信号时,不会马上触发,而是等槽函数所在线程进入事件循环后触发并且槽函数在接收方线程中运行。
3.阻塞队列连接BlockingQueuedConnection
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
MyThread* subThread = new MyThread(this);
subThread->start();
connect(this, SIGNAL(sig()), subThread, SLOT(on_do()), Qt::BlockingQueuedConnection);
//connect(this, &SignalSoltConnectionTypeDemo::sig, subThread,&MyThread::on_do, Qt::QueuedConnection);
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
qDebug() << QStringLiteral("触发信号后");
}
输出结果
"线程Id:" QThread(0x22919f27430)
"触发信号前"
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is SignalSoltConnectionTypeDemo(0x8513d8fc58), receiver is MyThread(0x22919f0d2e0)
"线程Id:" QThread(0x22919f27430)
"触发信号前"
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is SignalSoltConnectionTypeDemo(0x8513d8fc58), receiver is MyThread(0x22919f0d2e0)
从输出可以看出,不在同线程中Qt::BlockingQueuedConnection也造成了死锁,这是什么原因呢?
特殊情况
信号和槽与事件循环
当采用QueuedConnection连接方式时,且在同一线程中,槽函数的调用时机为当前线程进入事件循环。而不是等当前函数执行完就去调用(如果当前函数中调用了QCoreApplication::processEvents(QEventLoop::AllEvents);进入事件循环即可调用)。
SignalSoltConnectionTypeDemo::SignalSoltConnectionTypeDemo()
{
connect(this, SIGNAL(sig()), this,SLOT(on_do()),Qt::QueuedConnection);
//发送信号
qDebug() << QStringLiteral("线程Id:") << QThread::currentThread();
qDebug() << QStringLiteral("触发信号前");
emit sig();
QCoreApplication::processEvents(QEventLoop::AllEvents);
qDebug() << QStringLiteral("触发信号后");
}
void SignalSoltConnectionTypeDemo::on_do()
{
qDebug() << QStringLiteral("槽函数的线程Id:") << QThread::currentThread();
}
输出信息如下:
"线程Id:" QThread(0x21130be7700)
"触发信号前"
"槽函数的线程Id:" QThread(0x21130be7700)
"触发信号后"
参考
1.QT信号和槽连接方式小结
2.QT信号和槽机制
浙公网安备 33010602011771号