查看完整版本: [-- 程序运行一段时间后就出问题。 --]

QTCN开发网 -> Qt基础编程 -> 程序运行一段时间后就出问题。 [打印本页] 登录 -> 注册 -> 回复主题 -> 发表主题

介农酥 2019-08-03 14:19

程序运行一段时间后就出问题。

程序运行一段时间就出问题。目前已经知道是因为QMutex的问题,将这mutex.lock和mutex.unlock去掉就没问题(测试了5遍就是这个问题)。

先介绍下程序,程序基本过程如下:
1)socket线程从服务器获取数据;
2)socket将数据发给data线程解析数据,分别是快递单号expressId,小车号carrierId;
3)data将快递单号发给http线程,http根据快递单号获取到格口boxid;
4)http将boxid发回给data,data将数据expressid,carrierId和boxid发给数据库、plc和界面。
因为expressId、carrierid和boxid不是同时获取,为了防止在获得boxid的同时,expressId和carrierid被socket下一波数据覆盖掉,导致数据不一致,我在访问expressId和carrierid之前先加了锁mutex.lock,在获取boxid并且将数据发给plc和界面及数据库之后解锁mutex.unlock。其中mutex是data的成员变量,加锁和解锁在两个不同的槽函数里。

程序总共五个线程,一个主线程GUI,一个socket线程,一个data和数据库线程,一个http线程,一个plc线程。

代码片段
一、socket获取数据

void Socket::readServer()
{
    qDebug() << "-------------------------readServer--------------";
    startTime = QTime::currentTime();
    currentSocket = qobject_cast<QTcpSocket *>(sender());
    message = currentSocket->readAll();
    handleIndex = map.value(currentSocket);
    emit dataFromSocketSignal(message, handleIndex, startTime);   将数据发给data线程
    qDebug() << message;
}
二、data获取并解析数据

void DataHandle::reciveDataFromSocket(QString message, int handleIndex, QTime startTime)
{
    qDebug() << __FILE__ << __LINE__ << __FUNCTION__;
    mutex.lock(); //这里加锁
    carrierId = message.mid(carrierIdLength-3, 3);
    expressId = message.mid(carrierIdLength);
    this->startTime = startTime;
    this->handleIndex = handleIndex;
    emit getBoxidSignal(expressId); //将数据发给http
}
三、http获取expressId并且得到boxid

void Network::httpInRequest(QString expressId)
{ //接受数据并且向http请求
    qDebug() << "----------------------------" << __LINE__ << __FUNCTION__ << "-----------------------";
    initInPortStr();
    index = inPortStr.indexOf("billCode");
    inPortStr.insert(index + 11, expressId);
    setTimeStamp();
    index = inPortStr.indexOf("requestTime");
    inPortStr.insert(index + 13, timeStamp);
    qDebug() << __LINE__ <<  qPrintable(inPortStr);
    url.setUrl(inPortStr);
    request.setUrl(url);
    reply = manager->get(request);
    connectSignalSlot();
}


void Network::httpRead()
{ //读取http响应,即获得boxid
qDebug() << "----------------" << __func__ << __LINE__ << "-----------------";
    responseStr = reply->readAll();
qDebug() << qUtf8Printable(responseStr);
    beginBracketIndex = responseStr.indexOf("[");
    endBracketIndex = responseStr.indexOf("]");
    if (endBracketIndex - beginBracketIndex == 1)
        boxId = "";
    else
        boxId = responseStr.mid(beginBracketIndex + 2, endBracketIndex - beginBracketIndex - 3);
qDebug() << "格口:" << qPrintable(boxId);
    emit boxIdSignal(boxId); //将数据boxid又发回给data
}
四、data从http获取返回的数据
void DataHandle::getBoxId(QString boxId)
{
    ......
    emit plcDataSignal(carrierId.toShort(), boxId.toShort(), expressId, handleIndex, startTime);  //发送carrierId、expressid给pLc线程
    ....
    emit mainWindowDataSignal(carrierId, expressId, boxId, viewer); //将数据发给主线程界面
    ....
    db.insert(sql); //将数据插入数据库

    mutex.unlock(); //最后解锁,整个过程完成
}

问题描述:下面是打印出的数据

-------------------------readServer-------------- //socket读取数据
"*#11173117237677906"
..\expressSortSystemZTRepair20190803\data.cpp 43 reciveDataFromSocket //data接受数据
---------------------------- 62 httpOutRequest -----------------------  //http接受data的数据并发送http请求
73 http://172.31.28.11:8090/branchweb/sortservice?data={"billCode":"73117237677906","goodsHeight":0,"goodsLength":0,"goodsVolume":0,"goodsWidth":0,"incomeClerk":"","lineType":"","nextSiteId":0,"pipeline":"59771-003","requestTime":1564798858000,"scaleSn":"","scanMan":"","sortMode":"sorting","trayCode":"050","turnNumber":10,"weight":0}&data_digest=&msg_type=WCS_SORTING_INFO&company_id=zto
---------------- httpRead 82 -----------------  //http读取响应的数据boxId
{"message":"","result":{"billCode":"73117237677906","errorCode":"","isComp":false,"pipeline":"","scaleSn":"","sortPortCode":["255"],"sortSource":"0","trayCode":"050"},"status":true,"statusCode":"SUCCESS"}
格口: 255
getBoxId 54 ------------------getBoxId-------------------   //data从http获取数据并且发给PLC等
"INSERT INTO express_outflow_zt(EXPRESSFLOW_ID, STATUS, EXPRESS_SN, BOX_NUM, DELIVERY_TYPE, CREATE_TIME, User_id)VALUES('73117237677906', 0, '73117237677906', '255', 0, '2019-08-03 10:20:58', 'tato')" "111" "255" "73117237677906" 2 "内轨1"
135 getBoxId
plcWrite 111 73117237677906 255 2  //数据写入PLC
feedback urlStr: http://172.31.28.11:8090/branchweb/sortservice?data={"billCode":"73117237677906","pipeLine":"59771-003","sortPortCode":"255","sortSource":"0","sortTime":1564798858708,"turnNumber":1}&data_digest=&msg_type=WCS_SORTING_RESULT&company_id=zto
-------------------------readServer--------------   //socket读取数据
"*#11275164930244502"
..\expressSortSystemZTRepair20190803\data.cpp 43 reciveDataFromSocket  //data接受数据
---------------------------- 62 httpOutRequest -----------------------   //http接受data的数据并发送http请求
73 http://172.31.28.11:8090/branchweb/sortservice?data={"billCode":"75164930244502","goodsHeight":0,"goodsLength":0,"goodsVolume":0,"goodsWidth":0,"incomeClerk":"","lineType":"","nextSiteId":0,"pipeline":"59771-003","requestTime":1564798860000,"scaleSn":"","scanMan":"","sortMode":"sorting","trayCode":"050","turnNumber":10,"weight":0}&data_digest=&msg_type=WCS_SORTING_INFO&company_id=zto
---------------- httpRead 82 -----------------   //http读取响应的数据boxId
{"message":"","result":{"billCode":"75164930244502","errorCode":"","isComp":false,"pipeline":"","scaleSn":"","sortPortCode":["255"],"sortSource":"0","trayCode":"050"},"status":true,"statusCode":"SUCCESS"}
格口: 255
getBoxId 54 ------------------getBoxId-------------------  //data从http获取数据并且发给PLC等
plcWrite 112 75164930244502 255 2   //数据写入PLC
"INSERT INTO express_outflow_zt(EXPRESSFLOW_ID, STATUS, EXPRESS_SN, BOX_NUM, DELIVERY_TYPE, CREATE_TIME, User_id)VALUES('75164930244502', 0, '75164930244502', '255', 0, '2019-08-03 10:21:00', 'tato')" "112" "255" "75164930244502" 2 "内轨1"
feedback urlStr: http://172.31.28.11:8090/branchweb/sortservice?data={"billCode":"75164930244502","pipeLine":"59771-003","sortPortCode":"255","sortSource":"0","sortTime":1564798860708,"turnNumber":1}&data_digest=&msg_type=WCS_SORTING_RESULT&company_id=zto
135 getBoxId
-------------------------readServer--------------  下一波开始
不断重复,但是,运行运行着就下面这样了
就只读socket服务器了,后面就没了


"*#11475162853265075"
-------------------------readServer--------------
"*#11475162868879886"
-------------------------readServer--------------
"*#11173117237677906*#11275164930244502"    //这里数据连了
-------------------------readServer--------------
"*#11373117924080879*#11473117232028807"
plcWrite 114 73116654255618 255 2
-------------------------readServer--------------
"*#11478106488260665"
-------------------------readServer--------------
"*#11475162774107456"
-------------------------readServer--------------
"*#11473116629520024"
-------------------------readServer--------------
"*#11473116654255618*#11475162853265075"
-------------------------readServer--------------
"*#11475162868879886"
-------------------------readServer--------------
"*#11173117237677906"
-------------------------readServer--------------

关闭的时候,data线程也退不了

MainWindow::~MainWindow()
{
    qDebug() << __func__ << __LINE__;

    delete ui;
    socket->deleteLater();
    network->deleteLater();
    data->deleteLater();
    plc->deleteLater();

    qDebug() << __func__ << __LINE__;
    socketThread->quit();
    socketThread->wait();
    qDebug() << __func__ << __LINE__;
    httpThread->quit();
    httpThread->wait();
    qDebug() << __func__ << __LINE__;
    dataThread->quit();  
    dataThread->wait();  到这里就停了
    qDebug() << __func__ << __LINE__;
//    plcThread->quit();
//    plcThread->wait(1000);
    if (plcConnectSuccess) {
        plcThread->quit();
        plcThread->wait();
    } else {
        exit(0);
    }
    qDebug() << __func__ << __LINE__;


//  exit(0);
}

线程同步不是很懂,用的也少,难道我用错了?



liulin188 2019-08-03 15:26
估计是多释放了,释放了已经释放的锁会奔溃。

介农酥 2019-08-03 15:37
有办法避免?

介农酥 2019-08-03 15:37
liulin188:估计是多释放了,释放了已经释放的锁会奔溃。 (2019-08-03 15:26) 

有办法避免?

liulin188 2019-08-03 16:05
仔细检查下代码,我以前也遇到过这种问题。

liudianwu 2019-08-03 21:49
加了锁不解锁,作死!

介农酥 2019-08-04 10:05
liudianwu:加了锁不解锁,作死! (2019-08-03 21:49) 

加锁和解锁是在两个不同的函数中的,这样做不行吗?

介农酥 2019-08-04 10:06
liulin188:仔细检查下代码,我以前也遇到过这种问题。 (2019-08-03 16:05) 

加锁和解锁就前后两行代码,我想不出会哪里有误

firebolt 2019-08-04 11:03
介农酥:加锁和解锁是在两个不同的函数中的,这样做不行吗? (2019-08-04 10:05) 

也不是不行,但是你得保证能解除锁才行。如果你必须要这么用的话,请使用递归锁。

mmlgq 2019-08-04 13:39
加锁解锁用在两个函数没啥问题,在两个线程就问题,就是容易出现重复加解锁问题,一般尽量在一个函数完成,减少失误的几率。看了代码应该保证插入的一组 各种id一致就行,你这么多线程 信号和槽函数绑定的方式,我记得多线程的信号槽默认 是槽函数由自己的线程执行,你想保证一开始的carrierId  ,expressId id和后来的boxid一致 ,你信号槽 用参数一路带着就行了,不要加锁。你加锁本质上会导致加锁线程一致阻塞 直到你存数据的线程解锁。多线程效率没体现出来。个人见解,代码没仔细看。

介农酥 2019-08-04 20:15
mmlgq:加锁解锁用在两个函数没啥问题,在两个线程就问题,就是容易出现重复加解锁问题,一般尽量在一个函数完成,减少失误的几率。看了代码应该保证插入的一组 各种id一致就行,你这么多线程 信号和槽函数绑定的方式,我记得多线程的信号槽默认 是槽函数由自己的线程执行,你想保证一开 .. (2019-08-04 13:39)

谢谢!你这个一路带着所有参数是个好办法。你说的信号和槽的运行方式,我之前半生半懂的,现在看来是个问题,特意在百度搜了下:如下

-------------------------------------------------------------------------------------------------------------------------------
第五个参数代表槽函数在哪个线程中执行 :
1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。
2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行,即槽函数和信号发送者在同一线程
3)队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行,即槽函数与信号接受者在同一线程
4)锁定队列连接(QueuedConnection)
Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
5)单一连接(QueuedConnection)
Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接
---------------------
作者:52_赫兹的鲸
来源:CSDN
原文:https://blog.csdn.net/qq_40194498/article/details/79682811
版权声明:本文为博主原创文章,转载请附上博文链接!
--------------------------------------------------------------------------------------------------------------------------------
上面的解释可能对我的问题有参考价值,但正如你说的如果多个信号接连发过来,槽函数到底是怎么执行的呢。
比如线程A的信号a和线程B的槽函数b相连,现在线程A不断发送信号a,线程B是不断开子线程处理槽函数b,还是以队列的方式,一个一个的执行槽函数b,槽函数的所在的线程就是B吗?如果线程A同时发送两个不同的信号给B的两个槽函数,又是怎么怎么调用的?哪位大神能讲一下,connect第五个参数也不是解释这个的。

我自己写的程序就是不断的从摄像头上获取数据,也就是不断的要发送信号(全放在一个线程里也没问题,当然,是否影响性能,得线程测试了才知道,因为摄像头上的数据来了,就等着你接收,其实我问过写这个摄像头程序的人(就是服务端),他们的数据是一个个发的,当前的数据没发之前,后面来的数据先存在队列里,不过,我的程序有时候会连包,不过,我觉得应该是我写的程序的问题),将这些数据发给http后,http又是通过信号的形式将数据发给我,这就是一轮数据,接着不断这样。如果全放在一个线程里,也没问题,但是这头Socket要不断的接收数据,那头要从http服务器上接收返回的数据,这两个应该都是内部开了线程的,不会自动同步吧,感觉得了解里面的东西,谁能讲下,谢谢!

介农酥 2019-08-04 20:22
firebolt:也不是不行,但是你得保证能解除锁才行。如果你必须要这么用的话,请使用递归锁。 (2019-08-04 11:03) 

谢谢!递归锁不是很明白,我需要先了解下。到时候我再试试看。

介农酥 2019-08-05 11:22
综合大家的办法,我觉有这几种方法
一、connect第五个参数,这个应该可以,槽函数没有执行完,不允许在发射其他信号。
二、一路带着参数,这个应该不行,socket可以把参数带个data,data带个http,但是http自己的请求和响应的是框架给的,没办法带着参数,除非是有同步的http。
三、递归锁,这个不是很懂。
四、将socket、data、http放到一个线程,http做成同步的。
综合以上,第四钟比较保险,大家认为呢?

介农酥 2019-08-05 11:24
liulin188:仔细检查下代码,我以前也遇到过这种问题。 (2019-08-03 16:05) 

不是多释放了,是没解锁,data线程阻在那里了。

九重水 2019-08-05 11:41
又是你啊,上次的帖没拿到奖励,血亏!

介农酥 2019-08-05 12:51
九重水:[表情] 又是你啊,上次的帖没拿到奖励,血亏![表情] [表情]  (2019-08-05 11:41) 

这次你还是要亏

liulin188 2019-08-05 13:59
九重水:[表情] 又是你啊,上次的帖没拿到奖励,血亏![表情] [表情]  (2019-08-05 11:41) 

这钱有用吗

介农酥 2019-08-05 14:01
liulin188:这钱有用吗 (2019-08-05 13:59) 

兄弟们,都回答问题啊,在线急等啊


查看完整版本: [-- 程序运行一段时间后就出问题。 --] [-- top --]



Powered by phpwind v8.7 Code ©2003-2011 phpwind
Gzip disabled