一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

问题:
跨线程使用信号与槽连接,信号的发送时间间隔小于槽函数处理的时间间隔,造成的问题。

子线程下的槽函数,用sleep来模拟槽函数的耗时操作:

1 void MyThread::myTimeout()
2 {
3     qDebug() << "test";
4     QThread::sleep(2);
5 }

主线程下的信号发送函数,通过点击按钮来发送信号:

1 void Widget::on_buttonStart_clicked()
2 {
3     emit startThread();
4 }

线程之间的信号连接函数:

connect(this, &Widget::startThread, myT, &MyThread::myTimeout);

现象:在我们连续点击按钮后,能明显看到打印的信息有延时。

分析:在我们跨线程使用信号与槽时,connect函数默认是使用Qt::QueuedConnection队列的传输方式。当我们的槽函数处理不过来时,会先将传输的信号存入队列中,等槽函数处理完了再拿出来。

解决方法:
我们需要在等槽函数执行完后,才能发送新的信号。

方法一:使用connect的第五个参数,设置为Qt::BlockingQueuedConnection

槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

connect(this, &Widget::startThread, myT, &MyThread::myTimeout,Qt::BlockingQueuedConnection);

这个也是可以的,但是当我们子线程,处理所需时间很长时,
主线程会出现明显的卡顿,效果不太好。

方法二:
使用bool QObject::blockSignals(bool block)函数屏蔽信号发送,当槽函数处理完后,再开启。

子线程下的改动:

1 void MyThread::myTimeout()
2 {
3     qDebug() << "test";
4     QThread::sleep(2);
5     emit recover();
6 }

主线程下的改动:

 1 void Widget::on_recover()
 2 {
 3     ui->buttonStart->blockSignals(false);
 4 }
 5 
 6 void Widget::on_buttonStart_clicked()
 7 {
 8     emit startThread();
 9     ui->buttonStart->blockSignals(true);
10 }

增加信号连接函数:

1 connect(this, &Widget::startThread, myT, &MyThread::myTimeout);
2 connect(myT, &MyThread::recover, this, &Widget::on_recover);

通过增加对信号发送的限制,这样的话可以实现,只有在槽函数处理完成后,才会开始发送信号。

但是用这个函数有一个问题就是,这个对象的所有信号,在屏蔽的期间都不会发送了,也需要等槽函数处理完之后才能发送信号,实验代码如下。

 1 void Widget::on_buttonStart_clicked()
 2 {
 3     emit startThread();
 4     ui->buttonStart->blockSignals(true);
 5 }
 6 
 7 void Widget::on_buttonStart_pressed()
 8 {
 9     qDebug() << "信号能正常触发";
10 }

所以如果说两个对象之间只是一对一的信号连接的话,可以使用blockSignals函数

1、屏蔽信号的方式还可以用:

1 void MyThread::myTimeout()
2 {
3     QObject::sender()->blockSignals(true);
4     QThread::sleep(2);
5     QObject::sender()->blockSignals(false);
6 }

这样的话就可以直接在槽函数里,实现将发送信号的对象屏蔽和恢复。

先记录一下,后面有更好的方法再补充。。。

posted on 2022-08-22 16:39  一杯清酒邀明月  阅读(1928)  评论(0编辑  收藏  举报