线程池异步QWebSocket

本文介绍了如何使用Qt的线程池技术处理QWebSocket的异步通信,以避免主线程阻塞,提高数据传输效率。通过创建一个wsClientManager类,将多个QWebSocket实例放在同一线程中,实现了类似线程池的效果。示例代码展示了如何创建和管理这些线程,以及如何在不同线程间进行通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目的
我们可以使用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 <QDebug>
#include <QThread>
#include <QString>
#include <QObject>

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

#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();
}

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

mainwindow
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <worker.h>
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

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();
}

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

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.dowork();
    return a.exec();
}

运行结果如下图所示

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

#ifndef WSCLIENTMANAGER_H
#define WSCLIENTMANAGER_H

#include "worker.h"
#include <QMap>

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


实现文件如下:

#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,分别对应一个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();
}

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


void MainWindow::on_pushButton_clicked()
{
    QString str ;
    if(ui->radioButton->isChecked())
    {
        str = "qianbo1";
    }
    else
    {
        str = "qianbo2";
    }

    m_manager.send_message(str,ui->lineEdit->text());

}

根据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");
    
    });
});

启动如下所示:

在这里插入图片描述

无论谁发送过来都给一个this is a test,读者可以根据需求来做返回,也可以返回json等等,根据需求来做吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值