实际开发中慎用QCoreApplication::processEvents()。

实际开发中遇到问题然后处理问题是提高能力的最直接方式,笔者的文章都是在实际开发过程中发现问题然后去解决问题的过程,希望对读者有帮助。

这两天一直在处理一个程序崩溃的问题,大概的现象是程序跑起来没多久,就直接崩溃掉了,抓取过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)
{
    try
    {
        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);
        }
        else
        {
            m_devicemutex.unlock();
        }
    }

}
原文地址:https://www.cnblogs.com/joorey/p/14817726.html