多线程不允许操作UI!!!
场景:
在子线程的工作函数中,我想将错误以弹窗的形式显示出来,并qDebug一起打印出来,于是封装了一个Tools类,信号从Tools::msg( )中发出来,由Mainwindow接收,msg( )在子线程中调用。
问题:
1. 关于在多线程中调用QMessageBox弹窗的问题
2. 涉及多线程的connect对象问题
3. 涉及多线程的connect lamda表达式的注意事项
伪代码及分析:
//Tools类
class Tools
{
/**省略多余部分**/
public msg(const QString &str){
emit sigShowMsg(str);
qDebug(str);
}
}
//在Mainwindow中连接信号与槽函数
Tools tool;
connect(&tool, &Tools::sigShowMsg, [=](const QString &str){
QMessageBox::critical(this, "Error", str);
});
//在子线程中调用msg()函数,显示错误信息弹窗
Tools tool;
bool ret = work();
if(!ret){
tool.msg(str);
}
这时发现,当msg( )中qDebug信息被打印时,对应的错误弹窗并没有出现。也就是说信号发出,槽函数未执行。
首先排除了信号与槽在对象实例化之前进行连接而导致的槽函数不执行,难道是槽函数有问题?
我突然想到,Mainwindow中的tool对象和子线程中的tool对象并不是同一个,只是恰好名字相同,而在connect中被声明信号来源的tool就是在Mainwindow中从来没有发送信号的tool对象,而不是在子线程中调用错误弹窗提示msg( )的tool对象!(验证截图在文章最后)
于是乎,我将Tools类改写成饿汉式的单例模式:
connect(Tools::getInstance(), &Tools::sigShowMsg, [=](const QString &str){
QMessageBox::critical(this, "Error", str);
});
OK,弹窗触发了,但是又出现了新问题:
此时触发子线程中的错误弹窗将会出现QObject::setParent: Cannot set parent, new parent is in a different thread的输出信息
百度得知,在子线程操纵UI便会有次报错。可是在connect中,信号的来源是Tools,槽函数在主线程调用,为什么还会有这个问题呢?
罪魁祸首 ---> lamda表达式
看看下面两种写法有啥区别?
//槽函数在this(即MainWindow)调用
connect(Tools::getInstance(), &Tools::sigErrMsg, this, [=](const QString &str){
QMessageBox::critical(NULL, "Error", str);
});
//槽函数在信号来源处(子线程)调用
connect(Tools::getInstance(), &Tools::sigErrMsg, [=](const QString &str){
QMessageBox::critical(NULL, "Error", str);
});
在connect中指明了槽函数在this中执行后,我第一次觉得这个带着醒目红色叉叉的error弹窗是那么的顺眼!
所以在使用lamda表达式时,一定要注意槽函数的作用域!
最后献上验证截图,在lamda槽函数中打印了当前线程ID:
//主线程
connect(Tools::getInstance(), &Tools::sigLogInfo, this, [=](){
qDebug() << QThread::currentThreadId();
});
//子线程
connect(Tools::getInstance(), &Tools::sigLogInfo, [=](){
qDebug() << QThread::currentThreadId();
});