實際開發中慎用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();
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM