实际开发中遇到问题然后处理问题是提高能力的最直接方式,笔者的文章都是在实际开发过程中发现问题然后去解决问题的过程,希望对读者有帮助。
这两天一直在处理一个程序崩溃的问题,大概的现象是程序跑起来没多久,就直接崩溃掉了,抓取过dump文件用windbg去查具体的问题,但是没有任何效果,然后通过自己调试和测试大致知道问题出在什么地方,就是数据入库的时候导致了程序崩溃。那么就在这个地方下功夫去处理,在插入数据库的函数中发现开发者用到了QCoreApplication::processEvents()接口,然后就去查了一下这个函数的作用。
QCoreApplication::processEvents()的作用,作用就是处理密集型耗时的事情。
当我把入库函数中相关QCoreApplication::processEvents()的语句都屏蔽掉的时候。
int JNDBUtil::InsertTableData(QString tableName, const QVariantMap& params)
BMSLogger::GetInstance().OutputLog(LOG_INFO, "start to set sqlquery");
//QCoreApplication::processEvents(QEventLoop::AllEvents, 200);
QMap<QString, QVariant>::const_iterator c_iter;
QString property = "";
QString values = "";
for (c_iter = params.begin(); c_iter != params.end(); ++c_iter)
property += "`" + c_iter.key() + "`,";
values += (":" + c_iter.key() + ",");
//QCoreApplication::processEvents(QEventLoop::AllEvents, 200);
property = " ( " + property.mid(0, property.length() - 1) + " ) ";
values = "Values ( " + values.mid(0, values.length() - 1) + " ) ";
QString queryStr = "insert into " + tableName + property + values;
//QCoreApplication::processEvents(QEventLoop::AllEvents, 200);
return DBHandler::InsertTableData(queryStr, params);
运行程序,程序没有挂掉,但是程序出现假死的状态,界面根本无法操作,但是后台数据入库还是在进行中,这就说明了一点,数据入库和界面在同一个线程中,处理数据入库消息的时间太长了,界面没办法刷新。
上面也说了,如果我不屏蔽QCoreApplication::processEvents,那么程序在运行20秒左右的时候就会挂掉。
实际上在我们开发的过程中这样做是很不好的,界面刷新和数据入库的操作应该要做到分离,不能在同一个线程中去处理,于是我就将数据入库这部分的操作另外起了一个线程处理,在运行的时候程序就没有出现崩溃的现场。
InBoundThread.h
#pragma once
#include <QThread>
#include "jnstruct.h"
#include <QMap>
#include <QVector>
#include <QQueue>
class JNDBUtil;
class DeviceInfoWidget;
class ForRecoderInfoSt;
struct FormulaRecordInfo
QString RecordStepNo; //从工步配方表中获得的工步号
QString RecordTime; //从工步配方表中获得的记录时间
QString RecordCutOffSet; //从工步配方表中获得的截止设置
struct ForRecorderInfo
int chan;
ForRecoderInfoSt m_recordinfo;
class CInBoundThd : public QThread
Q_OBJECT
public:
CInBoundThd(DeviceInfoWidget *pDeviceInfoWgt);
~CInBoundThd();
static void InitOldMapData();
void GetDBFormula(const QString& formulaname, const QString& celltypename);
void GetRecorderInfoAndCNo(const ForRecoderInfoSt &st, int iChan);
private:
void InitMem();
//更新过程数据和截止数据到数据库
void UpdateDataToDB(const ForRecoderInfoSt &st, int iChan);
bool SaveCutOffData(const ForRecoderInfoSt &st, int iChan);
bool SaveProcessData(const ForRecoderInfoSt &st, int iChan);
protected:
void run() override;
private:
//DeviceInfoWidget *m_pDeviceInfoWgt;//主界面
static QMap<int, ForRecoderInfoSt> g_mapOldStThd;
QQueue<ForRecorderInfo> m_TmpFRecordInfo;
QVector<FormulaRecordInfo> m_VecFormulaRecordInfo;
JNDBUtil *m_pUtil;
ForRecoderInfoSt m_TmpRecordInfo;
int m_TmpChan;
InBoundThread.cpp
#include "InBoundThread.h"
#include "..\Common/bmslogger.h"
#include "..\Common/jndbutil.h"
#include "..\Common/globalconststr.h"
#include "..\Common/commFuc.h"
#include "..\OtherUI/CellBind/cellbindwidget.h"
#include <QDateTime>
QMap<int, ForRecoderInfoSt> CInBoundThd::g_mapOldStThd;
CInBoundThd::CInBoundThd(DeviceInfoWidget *pDeviceInfoWgt)
//m_pDeviceInfoWgt = pDeviceInfoWgt;
m_pUtil = new JNDBUtil;
InitMem();
CInBoundThd::~CInBoundThd()
SafeDelete(m_pUtil);
void CInBoundThd::InitOldMapData()
g_mapOldStThd.clear();
for (int i = 0; i < 40; i++)
g_mapOldStThd[i + 1] = ForRecoderInfoSt();
void CInBoundThd::GetDBFormula(const QString& formulaname, const QString& celltypename)
m_VecFormulaRecordInfo.clear();
QString sCon = QString("SELECT * FROM work_step_formula WHERE cellType = '%1' AND formulaName = '%2';").arg(celltypename).arg(formulaname);
auto list = m_pUtil->GetTableData(sCon);
for (auto item : list)
FormulaRecordInfo tmpFormulaInfo;
tmpFormulaInfo.RecordStepNo = item[GlobalConstStr::m_gFiled_stepNo].toString();
tmpFormulaInfo.RecordTime = item[GlobalConstStr::m_gFiled_recoderTime].toString();
tmpFormulaInfo.RecordCutOffSet = item[GlobalConstStr::m_gFiled_cutOffSet].toString();
m_VecFormulaRecordInfo.append(tmpFormulaInfo);
void CInBoundThd::GetRecorderInfoAndCNo(const ForRecoderInfoSt &st, int iChan)
ForRecorderInfo RecordInfo;
RecordInfo.chan = iChan;
RecordInfo.m_recordinfo = st;
//m_TmpFRecordInfo.append(RecordInfo);
m_TmpFRecordInfo.enqueue(RecordInfo);
void CInBoundThd::InitMem()
InitOldMapData();
void CInBoundThd::UpdateDataToDB(const ForRecoderInfoSt &st, int iChan)
if (g_mapOldStThd.contains(iChan))
auto &oldSt = g_mapOldStThd[iChan];
if (m_VecFormulaRecordInfo.size() == 0)
return;
//存入每个配方第一个工步的起始数据
if (st.stepNo == m_VecFormulaRecordInfo[0].RecordStepNo.toUInt() && st.iTime == m_VecFormulaRecordInfo[0].RecordTime.toInt() * 1000) // m_VecFormulaRecordInfo[0].RecordStepNo m_VecFormulaRecordInfo[0].RecordTime.toInt() * 1000
SaveCutOffData(st, iChan);
if (oldSt.stepNo != 0 && oldSt.stepNo != st.stepNo)
//存入截止数据
SaveCutOffData(oldSt, iChan);
if (st.stepNo != 0 && st.stepType != 0)
SaveCutOffData(st, iChan);
if (st.runMode == JN::EmRunState::emStepStart)
//存入过程数据
BMSLogger::GetInstance().OutputLog(LOG_INFO, "start to store processdata!"); //add by zhurui 2021/5/20
SaveProcessData(st, iChan);
oldSt = st;
catch (const std::exception&)
BMSLogger::GetInstance().OutputLog(LogLevel::LOG_ERROR, GetCurTime() + QString::fromLocal8Bit("UpdateDataToDB::更新数据到数据库失败!!!"));
bool CInBoundThd::SaveCutOffData(const ForRecoderInfoSt &st, int iChan)
BMSLogger::GetInstance().OutputLog(LOG_INFO, "start to store cutoffdata");
auto &cellInfo = CellBindWidget::GetCellInfo();
QString cellSN = cellInfo.value(iChan);
if (cellSN.isEmpty())
BMSLogger::GetInstance().OutputLog(LOG_ERROR, GetCurTime() + "->: " + QString::fromLocal8Bit("电芯截止数据记录失败,%1通道没有绑定电芯码!").arg(iChan));
return false;
int runTime = QDateTime::fromString(st.date, "yyyy-MM-dd hh:mm:ss.zzz").toTime_t();
QString id = QString("%1-%2-%3").arg(iChan).arg(cellSN).arg(runTime);
QVariantMap map;
map["id"] = id;
map["batteryCode"] = cellSN;
map["channelNo"] = iChan;
map["current"] = FormatNum(st.cur);
map["voltage"] = FormatNum(st.vol);
map["energy"] = FormatNum(st.power);
map["currentTime"] = st.date;
//add by zhurui 2021/4/8
map["cutoffstepNo"] = st.stepNo;
map["cutoffstepType"] = st.stepType;
map["cutoffcap"] = FormatNum(st.cap);
if (m_pUtil->InsertTableData("formation_cutoffdata", map) == -1)
BMSLogger::GetInstance().OutputLog(LOG_ERROR, GetCurTime() + "->: " + QString::fromLocal8Bit("电芯截止数据记录失败,%1通道").arg(iChan));
return false;
return true;
bool CInBoundThd::SaveProcessData(const ForRecoderInfoSt &st, int iChan)
auto &cellInfo = CellBindWidget::GetCellInfo();
QString cellSN = cellInfo.value(iChan);
if (cellSN.isEmpty())
BMSLogger::GetInstance().OutputLog(LOG_ERROR, GetCurTime() + "->: " + QString::fromLocal8Bit("电芯过程数据记录失败,%1通道没有绑定电芯码!").arg(iChan));
return false;
int runTime = QDateTime::fromString(st.date, "yyyy-MM-dd hh:mm:ss.zzz").toTime_t();
QString id = QString("%1-%2-%3").arg(iChan).arg(cellSN).arg(runTime);
QVariantMap map;
map["id"] = id;
map["batteryCode"] = cellSN;
map["channelNo"] = iChan;
map["current"] = FormatNum(st.cur);
map["voltage"] = FormatNum(st.vol);
map["capacity"] = FormatNum(st.cap);
map["energy"] = FormatNum(st.power);
map["batteryTemperature"] = FormatNum(st.temp);
map["ratio"] = FormatNum(st.curRat);
map["povl"] = FormatNum(st.outVol);
map["stepNo"] = st.stepNo;
map["stepType"] = /*m_formationProtocol.GetStepName(*/st.stepType/*)*/;
map["sumStep"] = st.accStep;
map["loopNo"] = st.loopNo;
//map["funcCode"] = st.loopNo;//没有
map["runState"] = st.runMode;
map["runTime"] = st.iTime;
map["currentTime"] = st.date;
if (m_pUtil->InsertTableData("formation_processdata", map) == -1)
BMSLogger::GetInstance().OutputLog(LOG_ERROR, GetCurTime() + "->: " + QString::fromLocal8Bit("电芯过程数据记录失败,%1通道").arg(iChan));
return false;
return true;
void CInBoundThd::run()
while (true)
if (m_TmpFRecordInfo.length() > 200)
ForRecorderInfo m_RunRecorderInfo;
m_RunRecorderInfo = m_TmpFRecordInfo.dequeue();
//UpdateDataToDB(m_TmpRecordInfo, m_TmpChan);
UpdateDataToDB(m_RunRecorderInfo.m_recordinfo, m_RunRecorderInfo.chan);
在deviceinfowidget.h中声明线程
CInBoundThd* m_pInBoundThd;
在deviceinfowidget.cpp中模块的构造函数中初始化
m_pInBoundThd = new CInBoundThd(this);
m_pInBoundThd->start();
析构函数中处理方式
if (m_pInBoundThd != nullptr)
m_pInBoundThd->terminate();
m_pInBoundThd->wait();
delete m_pInBoundThd;
欢迎各位大佬指正,这其中还有一个问题,就是为什么加上了QCoreApplication::processEvents()以后会挂?需要再去深究一下!
2021年6月1日修改
实际运行中发现一个问题,多线程同步的问题没有考虑到,就是我们常说的加锁处理,在本例中有两个线程对队列进行了操作,一个地方插入队列,一个地方取队列,所以这两个地方就需要加锁,如果按照楼主这样处理程序确实没有问题,但是队列中小于200条的记录
m_devicemutex.lock();
m_TmpFRecordInfo.enqueue(RecordInfo);
m_devicemutex.unlock();
void CInBoundThd::run()
while (true)
//msleep(300);
m_devicemutex.lock();
if (m_TmpFRecordInfo.size() > 0)
ForRecorderInfo m_RunRecorderInfo;
m_RunRecorderInfo = m_TmpFRecordInfo.dequeue();
//UpdateDataToDB(m_TmpRecordInfo, m_TmpChan);
m_devicemutex.unlock();
UpdateDataToDB(m_RunRecorderInfo.m_recordinfo, m_RunRecorderInfo.chan);
m_devicemutex.unlock();