• QT打造高效线程池异步QWebSocket 客户端


    目的

    我们可以使用QWebSocket得异步通信来高效传输数据,当时我们使用QWebSocket来和服务端取得视频信息流,而渲染也使用同样得界面线程,大量得包流通并且要渲染多个画面,这使得通信包来不及接收,这样,QWebSocket要使用不同得线程来接收数据包,和渲染分开,所以可以使用多个线程来将QWebSocket得异步通信放入到某个线程中,这里比较核心的是,将几个QWebSocke 放到同一个线程执行,这其实就是线程池技术。我们的示例是将两个QWebSocket放到一个线程中,学会了处理,自己稍加修改就可以做出像样的线程池了。

    qt 多线程websocket

    Qt有两种多线程的方法,其中一种是继承QThread的run函数,
    另外一种是把一个继承于QObject的类用moveToThread函数转移到一个Thread里。
    Qt4.8之前都是使用继承QThread的run这种方法,Qt4.8之后,Qt官方建议使用第二种方法。

    具体的使用步骤如下:

    1.从QObject派生一个类,将耗时的工作写在该类的槽函数中。

    2.将派生类对象移动到一个QThread中,该线程需要start。(这一步使用moveToThread)

    3.通过信号连接派生类的槽函数,并通过信号触发槽函数。(槽函数在子线程中执行)

    #ifndef WORKER_H
    #define WORKER_H
    
    
    #include 
    #include 
    #include 
    #include 
    
    class Worker:public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent=0);
        ~Worker();
    
    signals:
        void sig_finish();
    
    public slots:
        void slot_dowork();
    
    };
    #endif // WORKER_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    #include "worker.h"
    
    Worker::Worker(QObject *parent):QObject(parent)
    {
        qDebug()<<"worker()";
    }
    Worker::~Worker()
    {
        qDebug()<<"~worker()";
    }
    void Worker::slot_dowork()
    {
        qDebug()<< "do work,thread id = " << QThread::currentThreadId();
        int i =0;
        while(i++<10)
        {
            qDebug()<<"this is time for:"<<i;
            QThread::msleep(20);
        }
        emit sig_finish();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    其中我们的slot_dowork函数其实就是我们的线程工作函数,是通过发送信号来接收信息的,线面我们来看mainwindow

    mainwindow

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include 
    #include 
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
        void dowork();
    private:
        Ui::MainWindow *ui;
        Worker *m_pworker;
        QThread *m_pthread;
    signals:
        void sig_dowork();
    
    public slots:
        void slot_finish();
    };
    #endif // MAINWINDOW_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    mainwindow.cpp 文件

      实现

      #include "mainwindow.h"
      #include "ui_mainwindow.h"
      
      MainWindow::MainWindow(QWidget *parent)
          : QMainWindow(parent)
          , ui(new Ui::MainWindow)
      {
          ui->setupUi(this);
          m_pworker = new Worker();
            m_pthread = new QThread();
            m_pworker->moveToThread(m_pthread);
            qDebug()<< "start,thread id = " << QThread::currentThreadId();
      
            connect(m_pthread, &QThread::finished, m_pworker, &QObject::deleteLater);
            connect(this,SIGNAL(sig_dowork()),m_pworker,SLOT(slot_dowork()));
            connect(m_pworker,SIGNAL(sig_finish()),this,SLOT(slot_finish()));
      }
      
      MainWindow::~MainWindow()
      {
          delete ui;
          m_pthread->quit();
          m_pthread->wait();
      }
      
      void MainWindow::dowork()
      {
          m_pthread->start();
          emit sig_dowork();
      }
      
      void MainWindow::slot_finish()
      {
           qDebug()<< "finish,thread id = " << QThread::currentThreadId();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35

      在主函数里面启动线程得方法:
      调用mainwindow的dowork,事先这些资源都已经new出来了,可以开始工作

      #include "mainwindow.h"
      
      #include 
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
          MainWindow w;
          w.show();
          w.dowork();
          return a.exec();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      运行结果如下图所示
      在这里插入图片描述

      多个websocket 使用同一个线程

      如果多个QWebSocket 使用不同得线程,创建多个以上得thread就行,问题是要分配好线程,不宜过多,不宜过少,如何做到呢,下面我们来修改一下:
      我们创建一个wsclientmanager类,使得多个websocket在一个线程里运行,只要能这样做,就可以创建一个线程池,每个线程分配相应得QWebSocket。

      #ifndef WSCLIENTMANAGER_H
      #define WSCLIENTMANAGER_H
      
      #include "worker.h"
      #include 
      
      class wsClient:public QObject
      {
          Q_OBJECT
      private:
          QString m_name;
          //QThread *m_pthread = NULL;
      public:
         // void send_message(QString str, char* content);
          void send(QString str)
          {
              emit sig_send(str);
          }
          void start()
          {
              emit sig_start();
          }
      public:
      signals:
          void sig_start();
          void sig_send(QString str);
      public slots:
          void slot_finish()
          {
               qDebug()<< "finish,thread id = " << QThread::currentThreadId();
          }
      };
      
      class wsClientManager:public QObject
      {
          Q_OBJECT
         QMap<QString,wsClient*> map_ptr_Client;
         QMap<QString,Worker*> map_ptr_work;
         QThread* m_pthread = NULL;
      public:
         wsClientManager();
         ~wsClientManager();
         int add_client(QString str);
         int dec_client(QString str);
      
          void start();
          void send_message(QString str, QString content);
      };
      
      #endif // WSCLIENTMANAGER_H
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51

      实现文件如下:

      #include "wsclientmanager.h"
      
      
      
      wsClientManager::wsClientManager()
      {
          m_pthread = new QThread();
      }
      wsClientManager::~wsClientManager()
      {
          m_pthread->quit();
          m_pthread->wait();
      }
      
      void wsClientManager::start()
      {
      
          //emit sig_dowork();
      }
      
      int wsClientManager::add_client(QString str)
      {
          if(!m_pthread->isRunning())
              m_pthread->start();
          qDebug()<<m_pthread->isRunning();
          if(map_ptr_Client.find(str)== map_ptr_Client.end())
          {
              wsClient* client = new wsClient();
              Worker* work = new Worker(str);
              work->moveToThread(m_pthread);
              connect(client,SIGNAL(sig_start()),work,SLOT(slot_start()));
              connect(client,SIGNAL(sig_send(QString)),work,SLOT(send(QString)));
              connect(work,SIGNAL(sig_finish()),client,SLOT(slot_finish()));
              map_ptr_work.insert(str,work);
              map_ptr_Client.insert(str,client);
              client->start();
              return 1;
          }
          return 0;
      }
      void wsClientManager::send_message(QString str, QString content)
      {
          auto iter = map_ptr_Client.find(str);
          if(iter!=map_ptr_Client.end())
          {
              iter.value()->send(content);
              //iter->second->send(content);
          }
          else
              qDebug()<<"not find the name of "<<str;
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52

      界面如下:
      在这里插入图片描述
      在启动时先创建用户1和用户2,分别对应一个wsClient, 创建相应得Worker,并且关联,代码见addClient函数,我们点击用户1时, 发送得是用户1要发送得内容,同理用户2
      事先创建用户1 qianbo1 用户2 qianbo2

      void MainWindow::dowork()
      {
          m_manager.add_client("qianbo1");
          m_manager.add_client("qianbo2");
          //m_pthread->start();
          //emit sig_dowork();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      发送时,根据选择得用户来发送内容

      
      void MainWindow::on_pushButton_clicked()
      {
          QString str ;
          if(ui->radioButton->isChecked())
          {
              str = "qianbo1";
          }
          else
          {
              str = "qianbo2";
          }
      
          m_manager.send_message(str,ui->lineEdit->text());
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      根据qDebug 输出如下, 返回内容可以看出是不同得用户接收得
      在这里插入图片描述
      那么服务端在哪里呢?我们使用nodejs来写一个非常简单得代码,一下子就有了,我们使用ws来做服务端测试

      
      var WebSocketServer = require('ws').Server,
      wss = new WebSocketServer({ port: 8777 });
      wss.on('connection', function (ws) {
          console.log('client connected');
          ws.on('message', function (message) {
              console.log(message);
              ws.send("this is a test");
      	
          });
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      启动如下所示:
      在这里插入图片描述
      无论谁发送过来都给一个this is a test,读者可以根据需求来做返回,也可以返回json等等,根据需求来做吧。

      代码下载:下载

    • 相关阅读:
      使用前缀和数组解决“区间和查询“问题
      Springboot、maven 打包瘦身,去除依赖的jar【springboot外置jar、配置文件】
      [LWC] Components Communication
      css之 vertical-align用法详解
      山西电力市场日前价格预测【2023-09-13】
      PyCharm导入python项目
      insightface数据制作全过程记录更新
      开源项目自荐:截图工具(小、快、功能丰富)
      大厂排兵布阵NFT
      《MongoDB入门教程》第04篇 MongoDB客户端
    • 原文地址:https://blog.csdn.net/qianbo042311/article/details/126882915