记:子线程实现QTcpSocket读写的问题

最近在改进考勤系统客户端多线程实现时遇到了线程异步和野指针问题


client:多线程实现ui界面显示(主线程)、人脸检测(检测线程)、socket网络通信(通信线程)三个任务。

主线程:

  1. TimerEvent实时读取摄像头数据显示,并发送图像数据至detect_thread进行人脸检测
  2. QTimer定时发送信号至socket_thread建立TCP连接,连接成功则关闭定时器,断开连接则打开定时器(信号槽实现);
  3. 显示detect_thread检测的人脸框,显示socket_thread接收的来着server端返回的考勤信息。

检测线程:

  1. 人脸检测;
  2. 检测成功,送人脸框至主线程显示;(if detectFlag>0, detectFlag++) 若连续检测到5帧人脸(detectFlag>5),触发socket_thread向server端发送人脸图像
  3. 检测失败,(detectFlag=0)

网络通信线程:

  1. 基于QTcpSocket与server端创建TCP连接,得到socket通信对象;
  2. write发送人脸图像至sever端(detectFlag=-1);
  3. read接收server返回的考勤信息 。

PS:调试中出现了野指针问题,且由于线程异步运行造成的程序崩溃,原因如下

**1.野指针socket:**由于QTcpSocket对象只能在一个线程中创建及使用,而子线程的构造函数是运行在主线程下的,故我们只能在子线程 connect槽函数中给QTcpSocket对象分配内存(new)。于是我就水灵灵得忘记初始化QTcpSocket指针对象了,故子线程 connect槽函数运行之前,socket是个野指针。

**2.线程运行是异步的:**若检测线程先完成连续5次的人脸检测,触发通信线程调用socket的write接口发送数据,而此时socket还未创建连接, 没有分配内存,程序就崩溃了。

解决方法:

step1. 指针定义后一定要初始化!在检测线程的构造函数中初始化socket为nullptr;

cpp 复制代码
tcpCommunicate::tcpCommunicate(QMutex& mutexFlag, QObject *parent)
    :mutexFlag(mutexFlag), QObject(parent)
{
    qDebug()<<"tcpCommunicate() ID: "<<QThread::currentThreadId();
    tcpSocket = nullptr;
}

void tcpCommunicate::clientConnetToHost()
{
    qDebug()<<"tcpCommunicate slot threadID: "<<QThread::currentThreadId();
    if(!tcpSocket) tcpSocket = new QTcpSocket;
    tcpSocket->connectToHost("xxx.xxx.xxx.x", 22); //连接服务器
    qDebug()<<"正在连接服务器";

    connect(tcpSocket, &QTcpSocket::disconnected, this, [&](){
        tcpSocket->close();
        tcpSocket->deleteLater();
        emit tcpDisConnected();
    });
    connect(tcpSocket, &QTcpSocket::connected, this, [&](){
        emit tcpConnected();
    });
    connect(tcpSocket, &QTcpSocket::readyRead, this, &tcpCommunicate::readSocketData);
}

**step2.使用指针前先判断是否为空!**socket->write前,先判断socket是否为空,是否已经创建连接。

cpp 复制代码
void tcpCommunicate::sendSocketData(cv::Mat image, int* detectFlag)
{
    //qDebug()<<"sendSocketData";

    // JPEG压缩
    vector<uchar> buf;
    vector<int> params = {cv::IMWRITE_JPEG_QUALITY, 90};
    imencode(".jpg", image, buf, params);
    QByteArray byteBuf((const char*)buf.data(), buf.size()); //Mat->jpeg格式的QbyteArray

    // QDataStream 对数据串行打包
    quint64 bufSize = byteBuf.size();
    QByteArray sendData;    // QByteArray 作为缓冲区存储字节数据
    QDataStream sendStream(&sendData, QIODevice::WriteOnly); // 串行化流
    sendStream.setVersion(QDataStream::Qt_5_14);    // 设置Qt串行化版本
    sendStream<<bufSize<<byteBuf;   // 串行发送:数据长度->人脸数据

    // write
    /*  线程异步产生的问题:detect线程可能快于tcpCom线程 -> 在没有建立连接的情况下tcpSocket进行数据发送*/
    /*  PS:指针定义时先初始化为空,使用前先判断是否为空*/
    if(tcpSocket && tcpSocket->state() == QAbstractSocket::ConnectedState){
        //qDebug()<<"Send TCp";
        tcpSocket->write(sendData);

        mutexFlag.lock();
        *detectFlag = -1;
        mutexFlag.unlock();

    }else{
        qDebug()<<"disconn";
    }
}
相关推荐
Lee_jerome1 天前
《C/C++编译全家桶:g++四阶段、.a与.so区别、CMake构建、ARM交叉编译,这篇全讲透了》
c语言·开发语言·arm开发·c++·编译·cmake·cmakelists
冰心孤城1 天前
C++ 与 C#混合编程 示例 (基于VS)
java·c++·c#
Infedium1 天前
Qt嵌入式开发核心亮点:线程安全硬件交互技术
开发语言·qt
一只旭宝1 天前
C++手写shared_ptr共享智能指针|原子引用计数、强弱引用控制块、赋值重载底层深度剖析
开发语言·c++·面试
开发者联盟league1 天前
Java 通过 JNA 调用 C++ DLL:关键流程总结
java·c++·jna
库克克1 天前
【C++】智能指针
java·开发语言·c++
一拳一个呆瓜1 天前
【STL】iostream 编程:输入流成员函数
c++·stl
xiaoye-duck1 天前
《Linux系统编程》Linux 系统多线程(七): C++ 线程安全日志系统封装:基于策略模式解耦,兼容 glog 使用风格
linux·c++·日志系统
秋田君1 天前
QT_QInputDialog类输入对话框
开发语言·qt
程序喵大人1 天前
【C++进阶】STL容器与迭代器 - 07 迭代器是容器和算法之间的通用游标
开发语言·c++·算法