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

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

  • 前言
  • connect函数与QThread
    • 推荐方式
  • 方案

前言

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

connect函数与QThread

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

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

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

#include  
#include  
#include  
#include   
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  
#include  
#include  
#include  
 
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  
#include  
#include  
#include  
 
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的方式暂不介绍,有时间再整理。

你可能感兴趣的:(Qt,qt,udp,开发语言)