IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> QThread内创建QUDPSocket接收并处理数据 -> 正文阅读

[网络协议]QThread内创建QUDPSocket接收并处理数据

QThread内创建QUDPSocket接收并处理数据

前言

最近做项目,与仿真机通信。仿真机发送数据频率为1毫秒,导致Qt上位机在主线程频繁接收数据,造成界面卡死,因此将整个udp的通信和解析都放在线程中进行。

connect函数与QThread

connect函数最后一个参数指定了三种连接方式:自动模式、直连模式和队列模式。自动模式下,是使用的直连模式还是队列模式,主要看信号发出者和槽接收者是否在同一线程。

  • 如果sender和reciver对象在同一线程中被创建,则采用直连模式。
  • 如果sender和reciver对象在不同的对象中被创建,则采用队列模式。

直连模式: 是指当信号发送时,槽函数直接被调用,不管reciver是那个线程创建的,都在发射信号的线程内执行。换句话说,在子线程中创建sender对象,发送信号后,不管reciver是在主线程还是子线程创建,都会在子线程中执行。

#include <QtCore/QCoreApplication> 
#include <QtCore/QObject> 
#include <QtCore/QThread> 
#include <QtCore/QDebug>  
class Dummy:public QObject 
{ 
    Q_OBJECT 
public: 
    Dummy(){} 
public slots: 
    void emitsig() 
    { 
        emit sig(); 
    } 
signals: 
    void sig(); 
}; 
 
class Thread:public QThread 
{ 
    Q_OBJECT 
public: 
    Thread(QObject* parent=0):QThread(parent) 
    { 
        //moveToThread(this); 
    } 
public slots: 
    void slot_main() 
    { 
        qDebug()<<"from thread slot_main:" <<currentThreadId(); 
    } 
protected: 
    void run() 
    { 
        qDebug()<<"thread thread:"<<currentThreadId(); 
        exec(); 
    } 
}; 

#include "main.moc" 

int main(int argc, char *argv[]) 
{  
    QCoreApplication a(argc, argv); 
    qDebug()<<"main thread:"<<QThread::currentThreadId(); 
    Thread thread; 
    Dummy dummy; 
    QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main())); 
    thread.start(); 
    dummy.emitsig(); 
    return a.exec(); 
}

上面代码中,dummy和thread分别作为sender和reciver,都在同一个线程创建,采用直连模式,直接调用,所以slot_main()在主线程中执行。结果如下:

main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48

注意: 若想让slot_main()在子线程中执行,只需要将thread move到子线程中,改变thread的所在线程,采用队列模式。即将上面代码中“movetoThread(this);”解开注释即可。这种将线程对象移动到子线程的方案是强烈不推荐的。

队列模式: 是指sender发送信号后,信号被存到信号队列中,程序继续执行。直到reciver所在的线程事件循环处理到该信号时,槽函数被调用。槽函数在reciver对象所在的线程内执行。

#include <QtCore/QCoreApplication> 
#include <QtCore/QObject> 
#include <QtCore/QThread> 
#include <QtCore/QDebug> 
 
class Dummy:public QObject 
{ 
    Q_OBJECT 
public: 
    Dummy(QObject* parent=0):QObject(parent){} 
public slots: 
    void emitsig() 
    { 
        emit sig(); 
    } 
signals: 
    void sig(); 
}; 
 
class Thread:public QThread 
{ 
    Q_OBJECT 
public: 
    Thread(QObject* parent=0):QThread(parent) 
    { 
        //moveToThread(this); 
    } 
public slots: 
    void slot_thread() 
    { 
        qDebug()<<"from thread slot_thread:" <<currentThreadId(); 
    } 
signals: 
    void sig(); 
protected: 
    void run() 
    { 
        qDebug()<<"thread thread:"<<currentThreadId(); 
        Dummy dummy; 
        connect(&dummy, SIGNAL(sig()), this, SLOT(slot_thread())); 
        dummy.emitsig(); 
        exec(); 
    } 
}; 
 
#include "main.moc" 
 
int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    qDebug()<<"main thread:"<<QThread::currentThreadId(); 
    Thread thread; 
    thread.start(); 
    return a.exec(); 
}

代码中,thread在主线程中创建,dummy在子线程中创建,采用的是队列模式,所以槽函数slot_thread()其实在主线程中运行。解决方法有两个:

  • 一是将thread move到子线程,sender和reciver在同一线程,该方案同样不推荐。
  • 二是指定连接模式为直接模式,直连模式下,槽必然在sender所在的线程执行。只要做好sender对象和槽函数的线程同步。

推荐方式

定义一个普通的QObject派生类,然后将其对象move到QThread中。使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。

#include <QtCore/QCoreApplication> 
#include <QtCore/QObject> 
#include <QtCore/QThread> 
#include <QtCore/QDebug> 
 
class Dummy:public QObject 
{ 
    Q_OBJECT 
public: 
    Dummy(QObject* parent=0):QObject(parent)     {} 
public slots: 
    void emitsig() 
    { 
        emit sig(); 
    } 
signals: 
    void sig(); 
}; 
 
class Object:public QObject 
{ 
    Q_OBJECT 
public: 
    Object(){} 
public slots: 
    void slot() 
    { 
        qDebug()<<"from thread slot:" <<QThread::currentThreadId(); 
    } 
}; 
 
#include "main.moc" 
 
int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    qDebug()<<"main thread:"<<QThread::currentThreadId(); 
    QThread thread; 
    Object obj; 
    Dummy dummy; 
    obj.moveToThread(&thread); 
    QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot())); 
    thread.start(); 
    dummy.emitsig(); 
    return a.exec(); 
}

方案

在线程中创建UDPserver,接收数据并解析,解析完毕后,发送信号给主线程,进行界面更新。
子线程:

void HandleDatasThread::run()
{
    qDebug()<<"zixiancheng:"<<QThread::currentThreadId();
     QUdpSocket server; //用于接收数据
     quit = false;
     server.bind(port, QUdpSocket::ShareAddress);
     while(!quit)
     {
         if (server.hasPendingDatagrams())
         {
             QByteArray datagram;
             datagram.resize(int(server.pendingDatagramSize()));
             server.readDatagram(datagram.data(), datagram.size());

             qDebug()<<datagram.toHex();
             parseRecvData(datagram);
             emit updatetable();
         }
     }
}

主线程:

 connect(&m_calculationThread,SIGNAL(updatetable()),this,SLOT(computingFinished()));
void MainWindow::computingFinished()
{
    recvVars = m_calculationThread.getVars();
    if(QDateTime::currentMSecsSinceEpoch() / 1000 -lasttime>2)
    {
        setTableWidget(ui->recvTableWidget, recvVars);
        lasttime = QDateTime::currentMSecsSinceEpoch() / 1000;
    }
}

子线程中发送信号,主线程中处理,因此是队列模式,槽函数会在主线程中运行,从而更新界面。

通过普通的Object,movetothread的方式暂不介绍,有时间再整理。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-10 12:45:58  更:2021-11-10 12:46:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:49:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码