关于QT信号槽机制的第五个参数
之前有关注过这个第五个参数的内容,但由于平时使用过程中都默认不写,也没特别仔细研究,直到今天一个同事问我,才仔细研究了一下含义。
其实网上有很多有关博客,讲解的内容也都很正确,但是就是没那么好理解,如下图所示,参考文章
其实里面最主要的4种方式有:Qt::AutoConnection(自动),Qt::DirectConnection(同步),Qt::QueuedConnection(异步),Qt::BlockingQueuedConnection(阻塞连接)。
第五个参数实际意义
通过阅读别人的博客,可以理解到,其实第五个参数控制的是,你原本想在子线程执行的逻辑到底在哪执行,虽然听起来很绕,但是QT确实允许你将子线程的任务挪到主线程去执行。
多线程信号和槽
个人认为,抛开QT的多线程单独去谈信号和槽的第五个参数是完全不合理的,因为第五个参数的使用与多线程的工作模式息息相关。
在QT中,多线程的写法一般有2种,早先的QT是通过创建一个继承了Qthread
的类使用,而在较新的QT中则是采用movetothread
的方式去实现的,而信号和槽的五个参数影响的则是通过movetothread
实现的多线程工作方式,意识到这点至关重要,因为我就是在早期QT的多线程写法中去理解第五个参数,怎么试都没有得到逍遥的结果TAT。
新旧多线程对比
旧版多线程
我们以Qt::QueuedConnection
举个例子,如果按照别的博客上的说法,Qt::QueuedConnection
让槽函数运行于信号接受者所在线程,为了验证这个功能,我们进行如下设计。
我们设计一个按钮,按钮发送信号,这个信号同时连接本线程内的一个槽函数和其他线程的一个槽函数,相当于同一个信号挂到两个槽:
connect(this,SIGNAL(ButtonSend(int)),thread,SLOT(ButtonReceive(int)),Qt::QueuedConnection);
connect(this,SIGNAL(ButtonSend(int)),this,SLOT(ButtonReceive2(int)));
本线程的信号函数名称为ButtonSend
,通过按钮点击实现:
void MainWindow::on_pushButton_clicked()
{
emit ButtonSend(++i);
}
子线程的槽函数名称为ButtonReceive
,我们让他sleep 2秒,实现方式为:
void MThread::ButtonReceive(int i )
{
qDebug()<<"i am son , i got "<<i;
qDebug()<<"sleeping...";
sleep(2);
qDebug()<<"end ";
}
本线程的槽函数名称为ButtonReceive2
,实现方式为:
void MainWindow::ButtonReceive2(int i)
{
qDebug()<<"i am father, i got "<<i;
}
因此,Qt::QueuedConnection
实现的效果应该是,子线程和主线程各自执行,互不打搅,但实际运行起来确是:
主线程的槽函数完全是在子线程结束后才执行的,为什么会这样呢?其实在老版的多线程写法中,QThread其实是管理线程的工具,它是属于主线程的,也是在主线程中调用,就像调用了QThread类中的一个方法,因此会按照槽的顺序去执行,所以Qt::QueuedConnection
并没有生效。
新版多线程
当我们改用新版多线程子再去试一下:
主线程构造函数如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_threadRunInBack = new QThread(this);
m_myThread = new MyThread();
connect(this,&MainWindow::threadRun1,m_myThread,&MyThread::ButtonReceive2,Qt::QueuedConnection);
connect(this,SIGNAL(threadRun1(int)),this,SLOT(ButtonReceive(int)),Qt::QueuedConnection);
m_myThread->moveToThread(m_threadRunInBack);
}
signal函数如下
void MainWindow::on_pushButton_clicked()
{
m_threadRunInBack->start();
emit threadRun1(++i);
}
主线程槽函数如下:
void MainWindow::ButtonReceive(int i)
{
qDebug()<<"i am father, i got "<<i;
}
子线程槽函数如下:
void MyThread::ButtonReceive2(int i){
qDebug()<<"i am son , i got "<<i;
qDebug()<<"sleeping...";
sleep(2);
qDebug()<<"end ";
}
再看下运行效果:
恰如预期。
进一步验证Qt::DirectConnection
的效果
验证Qt::DirectConnection
效果,Qt::DirectConnection
是同步调用方式,及在信号发生者的线程去调用,在新版多线程写法中重复上述实验,因为槽的顺序是先子线程后主线程,因此打印顺序应该是先i am son 后 i am dad。
修改connect函数:
connect(this,&MainWindow::threadRun1,m_myThread,&MyThread::ButtonReceive2,Qt::DirectConnection);
connect(this,SIGNAL(threadRun1(int)),this,SLOT(ButtonReceive(int)));
运行看下结果:
正如所想,为什么跨线程要使用Qt::QueuedConnection
的异步方式呢?个人理解为害怕子线程崩溃影响到主线程运行?或者子线程任务过于庞大,影响到主线成ui。
希望对大家有帮助!