目录导读
方向一:bug问题描述
方向二:bug解决过程
还原造成死锁bug异常的类结构:
-
创建 Dal_DownData 下载类
Dal_DownData.h:
//! 定义一个下载类 伪代码
class Dal_DownData:public QObject
{
Q_OBJECT
public:
Dal_DownData();
void StartDown(QString file="text");
Q_SIGNALS:
///开始下载
void IsStart(bool bol);
/// 直接进度条设置业务值(百分值)
void ProgressBar(int value);
/// 直接进度条设置业务值(百分值)
void StyleStr(QString type);
};
Dal_DownData.h:
#include <QDebug>
Dal_DownData::Dal_DownData()
{
}
void Dal_DownData::StartDown(QString file)
{
emit IsStart(true);
for(int i=1;i<=100;i++)
{
//! 作为下载的一个进度效果
emit ProgressBar(i);
if(PROGRESSBAR!=nullptr)
PROGRESSBAR(i);
emit StyleStr("ACTIVE");
// std::this_thread::sleep_for(std::chrono::seconds(1));
Sleep(500);
}
emit IsStart(false);
}
-
创建 QThread_Down 线程基类
QThread_Down.h
//! 一个下载线程
class QThread_Down:public QThread
{
Q_OBJECT
public:
QThread_Down(QObject* parent=nullptr);
//! 修改操作1
void Function1();
//! 修改操作2
void Function2();
Q_SIGNALS:
///开始
void IsStart(bool bol);
void SendMessStr(QString str);
protected:
Dal_DownData* Down=nullptr;
};
QThread_Down.cpp
QThread_Down::QThread_Down(QObject* parent)
:QThread(parent)
{
Down=new Dal_DownData();
connect(Down,&Dal_DownData::IsStart,this,[&](bool bol){
if(bol)
emit QThread_Down::SendMessStr("开始下载...");
else
emit QThread_Down::SendMessStr("下载结束...");
},Qt::BlockingQueuedConnection);
connect(Down,&Dal_DownData::ProgressBar,this,[&](int val){
emit QThread_Down::SendMessStr(QString("\r正在下载:[%1%]...").arg(val));
},Qt::BlockingQueuedConnection);
connect(Down,&Dal_DownData::StyleStr,this,[&](QString val){
emit QThread_Down::SendMessStr(QString("\r下载状态:[%1]...").arg(val));
},Qt::BlockingQueuedConnection);
}
void QThread_Down::Function1()
{
msleep(500);
}
void QThread_Down::Function2()
{
msleep(500);
}
-
创建 QThread_Operation 线程类
线程类 QThread_Operation 继承 QThread_Down 线程类。
QThread_Operation.h
class QThread_Operation:public QThread_Down
{
Q_OBJECT
public:
QThread_Operation(QObject* parent=nullptr);
void run() override;
};
QThread_Operation.cpp
QThread_Operation::QThread_Operation(QObject* parent)
:QThread_Down(parent)
{
}
void QThread_Operation::run()
{
emit IsStart(true);
emit SendMessStr("开始线程...");
emit SendMessStr("等待三秒后开始调用下载...");
// std::this_thread::sleep_for(std::chrono::seconds(3));
msleep(500);
Down->StartDown();
emit SendMessStr("选择需要执行的操作等等...");
Function1();
// std::this_thread::sleep_for(std::chrono::seconds(1));
msleep(500);
emit SendMessStr("结束线程...");
emit IsStart(false);
}
还原造成死锁bug异常的具体操作:
在测试的时候我是使用的控制台程序输出结果:
最开始的时候,我正常连接信号,因为控制台程序没有this变量,所以就没有修改连接信号槽的方式。
QThread_Operation* operation=new QThread_Operation() ;
QObject::connect(operation,&QThread_Operation::IsStart,[&](bool bol){
if(bol)
qDebug()<<"线程启动!";
else
qDebug()<<"线程结束!";
});
QObject::connect(operation,&QThread_Operation::SendMessStr,[&](QString Str){
qDebug().noquote()<<Str;
});
operation->start();
结果正常输出结果,没有出现死锁,
我以为是我改用了 std::this_thread::sleep_for(std::chrono::seconds(3))
暂停线程的问题,于是改用MScv编译器使用 Sleep(500)
暂停线程。
结果输出结果依旧正常。
于是修改进度大小,添加中转的信号个数,依旧没有问题,
返回项目环境测试还是有死锁,不是偶发事件。继续修改测试
直到我为了与原版内容结构一致,添加了Qt::BlockingQueuedConnection
信号
//a是指QCoreApplication a(argc, argv);
QObject::connect(operation,&QThread_Operation::SendMessStr,&a,[&](QString Str){
qDebug().noquote()<<Str;
},Qt::BlockingQueuedConnection);
出现死锁:
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is QThread_Operation(0x22b5ce12b90), receiver is QCoreApplication(0x9a027ef870)
有点疑惑,自认为对于Qt::BlockingQueuedConnection
连接方式我还是有点心得的,还相到还能出个岔子,直到我找到了以前文章中摘抄的一段关于信号连接类型的说明:
于是我移除Dal_DownData类与QThread_Down类绑定的中转信号的连接方式
Down=new Dal_DownData();
connect(Down,&Dal_DownData::IsStart,this,[&](bool bol){
if(bol)
emit QThread_Down::SendMessStr("开始下载...");
else
emit QThread_Down::SendMessStr("下载结束...");
});
connect(Down,&Dal_DownData::ProgressBar,this,[&](int val){
emit QThread_Down::SendMessStr(QString("\r正在下载:[%1%]...").arg(val));
});
connect(Down,&Dal_DownData::StyleStr,this,[&](QString val){
emit QThread_Down::SendMessStr(QString("\r下载状态:[%1]...").arg(val));
});
还是死锁,只有移除QThread_Operation线程类与控制台程序的 Qt::BlockingQueuedConnection 连接方式,才正常输出。
我估摸着信号的接收者和发送者也没在同一线程上,咋还成死锁了,
盲猜可能就是QThread_Operation线程堵塞的时候下载线程输出的信号还再传递,所以修改为通过 回调函数
的方式传递信号,线程堵塞时都被暂停了,这样即使再使用Qt::BlockingQueuedConnection 信号连接依旧正常输出.