• 实际开发中慎用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();
            }
        }
    
    }
  • 相关阅读:
    贝叶斯分类
    K-Means算法
    python数组
    深度学习与神经网络
    数据挖掘算法之-关联规则挖掘(Association Rule)
    k8s记录-pip源配置
    k8s记录-yum本地仓库部署
    k8s记录-不同集群服务互联
    k8s记录-kube-dns(core-dns)配置(七)
    k8s记录-kubectl常用
  • 原文地址:https://www.cnblogs.com/joorey/p/14817726.html
Copyright © 2020-2023  润新知