Qt-系统相关(2)多线程网络

news2025/5/15 7:36:50

Qt多线程

在 Qt 中,多线程的处理⼀般是通过 QThread类 来实现。
QThread 代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据。
QThread 对象管理程序中的⼀个控制线程。

QThread 常⽤ API:

 

使用线程 

关于创建线程的步骤:

1. ⾃定义⼀个类,继承于 QThread,并且只有⼀个线程处理函数(和主线程不是同⼀个线程),这个线程处理函数主要就是重写⽗类中的 run() 函数。
2. 线程处理函数⾥⾯写⼊需要执⾏的复杂数据处理;
3. 启动线程不能直接调⽤ run() 函数,需要使⽤对象来调⽤ start() 函数实现线程启动;
4. 线程处理函数执⾏结束后可以定义⼀个信号来告诉主线程;
5. 最后关闭线程。

用多线程实现定时器功能

 

创建一个新的类:

记得勾选下面的 add Q_OBJECT 

然后重写这个类的run函数

也定义了一个信号

然后在Widget构造函数中初始化,还有定义槽函数

 执行结果:

关于这个start函数,这个才是真正调用系统API创建线程,新的线程创建出来后,会自动的执行run函数。

可以使用一个wait,让一个线程等待另一个线程的结束。

另外之前也说过,只有主线程才能针对界面的控件进行状态的修改。

理解Qt的多线程 

我们之前学过的多线程,是站在服务器的角度上来看待的,对于服务器来说:多线程最主要的目的就是要充分利用多核CPU的计算资源。

而对于客户端来说,多线程仍然非常有意义,但是与服务器的侧重点不同。

对于普通用户来说,“使用体验”是非常重要的话题。

客户端上的程序很少会使用多线程把计算资源吃完。

相比之下,客户端的多线程主要是通过多线程的方式来执行一些耗时的IO操作,这样可以避免主线程被卡死,造成不好的用户体验。比如我们用迅雷下载文件,当文件很大,要20分钟才能下载完,那么在下载的过程中,用户的界面不可能得一直阻塞住吧?

因此,我们可以使用一个单独的新线程来处理这种IO密集的操作。

线程安全问题 

谈到线程,始终绕不开的就是线程安全问题。

在Qt中实现线程互斥和同步常⽤的类有: 

互斥锁:QMutex、QMutexLocker
条件变量:QWaitCondition
信号量:QSemaphore
读写锁:QReadLocker、QWriteLocker、QReadWriteLock

简单使用互斥锁 

场景:两个线程对一个变量进行++,每个线程执行5000次。

重写run函数

 初始化操作

结果:

 对于Qt的互斥锁

条件变量 

在 Qt 中,专⻔提供了 QWaitCondition类 来解决像上述这样的问题。
特点:QWaitCondition 是 Qt 框架提供的条件变量类,⽤于线程之间的消息通信和同步。

使用示例:

QMutex mutex;
QWaitCondition condition;
//在等待线程中
mutex.lock();
//检查条件是否满⾜,若不满⾜则等待
while (!conditionFullfilled()) 
{
 condition.wait(&mutex); //等待条件满⾜并释放锁
}
//条件满⾜后继续执⾏
//...
mutex.unlock();
//在改变条件的线程中
mutex.lock();
//改变条件
changeCondition();
condition.wakeAll(); //唤醒等待的线程
mutex.unlock();

信号量 

特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
⽤途:限制并发线程数量,⽤于解决⼀些资源有限的问题。

示例:

QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进⾏类似操作

Qt网络 

 和多线程类似, Qt 为了⽀持跨平台, 对⽹络编程的 API 也进⾏了重新封装

在进⾏⽹络编程之前, 需要在项⽬中的 .pro ⽂件中添加 network 模块.
添加之后要⼿动编译⼀下项⽬, 使 Qt Creator 能够加载对应模块的头⽂件.

UDP Socket 

主要的类有两个. QUdpSocket QNetworkDatagram 

QUdpSocket 表⽰⼀个 UDP 的 socket ⽂件。 

QNetworkDatagram 表⽰⼀个 UDP 数据报 

UDP回显服务器 

一般一个正经的服务器是很少会带有图形化界面的,一般都是命令行。

服务器部分 

 用一个listWidget来显示信息

 初始化部分:

主要执行逻辑:

客户端部分 

界面部分

初始化代码:

按钮对应的槽函数实现:

再次修改Widget构造函数,通过信号槽来处理服务器的响应

 

用lambda表达式的方式

客户端演示:

服务器演示:

我们也可以启动多个客户端向服务器发起请求。 

关于这里面的一些细节 

在传参的时候关于什么时候用 引用类型,什么时候用值类型:

大的原则上是尽量使用引用类型。

但是有些时候,比如不同类型相互转换的时候,大概率使用值类型

两个问题:

这个Udp服务器是否能放到Linux云服务上呢?

大概率不行。这取决于这个云服务器是否安装了图形化界面,Qt程序需要依赖图形化界面来运行。

能否用现在的Udp客户端连接云服务器上linux的Udp服务器?

是可以的。

一般的商业公司都不会用Qt编写服务器,但是会用Qt编写客户端。 

 

TCPSocket

 核⼼类是两个: QTcpServer QTcpSocket

QTcpServer ⽤于监听端⼝, 和获取客⼾端连接. 

 关于QTcpServer:

 

QTcpSocket ⽤⼾客⼾端和服务器之间的数据交互 

 关于QTcpSocket:

QByteArray ⽤于表⽰⼀个字节数组. 可以很⽅便的和 QString 进⾏相互转换.
例如:
使⽤ QString 的构造函数即可把 QByteArray 转成 QString.
使⽤ QString 的 toUtf8 函数即可把 QString 转成 QByteArray

 

 TCP回显服务器

服务器部分 

 初始化:

 处理新连接的槽函数:

这里跟UDP就有明显不同了,主要是对连接的处理

void Widget::processConnection()
{
    // 注意和UDP的区别
    // 1.先获取新连接对应的socket
    QTcpSocket* clientSocket = server->nextPendingConnection();
    // 构建日志
    QString log = "客户端:" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "上线了";
    ui->listWidget->addItem(log);

    // 2.设置连接可读就绪的槽函数
    connect(clientSocket,&QTcpSocket::readyRead,clientSocket,[=](){
        // 读取请求
        QString request = clientSocket->readAll();
        // 构建响应
        QString response = prase(request);
        // 返回响应
        clientSocket->write(response.toUtf8());

        // 打印日志
        QString log = "客户端:" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + " 说: " + response;
        ui->listWidget->addItem(log);
    });

    // 3.设置连接断开时的槽函数
    connect(clientSocket,&QTcpSocket::disconnected,clientSocket,[=](){
        // 打印日志
        QString log = "客户端:" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "下线了";
        ui->listWidget->addItem(log);
        clientSocket->deleteLater(); // 下一个事件循环再删除
    });
}

QString Widget::prase(QString &str)
{
    return str;
}

客户端部分 

客户端界面:

 

 初始化:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->setWindowTitle("Tcp客户端");

    socket = new QTcpSocket(this);

    socket->connectToHost(ip,port); // 和服务器建立连接

    // 等待连接是否出错
    if(!socket->waitForConnected())
    {
        QMessageBox::critical(this,"连接出错",socket->errorString());
        exit(1);
    }

    // 处理连接可读就绪槽函数
    connect(socket,&QTcpSocket::readyRead,socket,[=](){
        QString response = socket->readAll();
        // 这里省略了解析过程
        QString log = "服务器说: " + response;
        ui->listWidget->addItem(log);
    });
}

 按钮槽函数的设计

void Widget::on_pushButton_clicked()
{
    QString text = ui->lineEdit->text();
    if(text.isEmpty()) return;
    ui->lineEdit->clear();
    socket->write(text.toUtf8());
    // 打印日志
    QString log = "客户端说: " + text;
    ui->listWidget->addItem(log);
}

演示效果:

服务器:

这里也是支持多个客户端同时连接的。

客户端:

 服务器能发送响应,客户端也能接收响应。

关于这里的一些细节

在TCP服务器处理请求这里,还是不够严谨的,因为TCP是字节流传输的,所以会存在数据“粘包”问题,这里我们并没有对其进行处理。这里严谨的做法应该是:将数据放到一个大的接收缓冲区中,然后约定好应用层协议的格式(报文定长,特殊字符等等)。

关于服务器那里,对于每一个客户端上线时,都会创建一个socket,随着客户端越来越多,如果这个socket不释放的话,就会造成内存泄漏,还有更严重的文件描述符泄漏。

我们可以通过delete手动释放,但是我们要考虑这个delete一定得被执行到,不会被return 异常这些情况而导致没有执行到。

Qt给了一种“半自动”的垃圾回收机制,

上述这个操作,不是立即销毁clientSocket,而是告诉Qt在下一轮事件循环中,再进行上述的销毁操作。

 

 HTTP Client

Qt提供了HTTP客户但,但是没有提供HTTP服务器的库。

原因也很简单,一个正经的服务器是不需要图形化界面的,也就是不会用Qt来开发。

HTTPClient关键类主要是三个. QNetworkAccessManager , QNetworkRequest , QNetworkReply 

QNetworkAccessManager 提供了 HTTP 的核⼼操作:

 

QNetworkRequest 表⽰⼀个 HTTP 请求(不含 body):

如果需要发送⼀个带有 body 的请求(⽐如 post), 会在 QNetworkAccessManager 的 post ⽅法
中通过单独的参数来传⼊ body

 

 

其中的 QNetworkRequest::KnownHeaders 是⼀个枚举类型, 常⽤取值: 

 

QNetworkReply 表⽰⼀个 HTTP 响应. 这个类同时也是 QIODevice 的⼦类:

 

此外, QNetworkReply 还有⼀个重要的信号 finished 会在客⼾端收到完整的响应数据之后触发

 

发送get请求 

客户端界面:

 这里我们以请求百度首页为例,得到的响应大概率是一个HTML格式的文件,这里我们的QplainTextEdit可以看到响应原始的模样。而QTextEdit天然会对HTML进行解析。

 初始化:

slot槽函数

void Widget::on_pushButton_clicked()
{
    QUrl url = ui->lineEdit->text();
    if(url.isEmpty()) return;
    ui->lineEdit->clear();
    // 构建http请求对象
    QNetworkRequest request(url);
    // 获取http请求响应
    QNetworkReply* response =  manager->get(request);
    // 构建槽函数来处理响应
    connect(response,&QNetworkReply::finished,response,[=](){
        if(response->error() == QNetworkReply::NoError)
        {
            // 响应正确
            QString html = response->readAll();
            ui->plainTextEdit->setPlainText(html);
        }
        else
        {
            // 响应出错
            ui->plainTextEdit->setPlainText(response->errorString());
        }
        response->deleteLater(); // 记得销毁
    });
}

 运行结果:

发送 POST 请求代码也是类似. 使⽤ manager->post() 即可 

 

Qt音频 

这里简单介绍一下Qt的音频

在 Qt 中,⾳频主要是通过 QSound 类来实现。但是需要注意的是 QSound 类只⽀持播放 wav 格式的⾳频⽂件。也就是说如果想要添加⾳频效果,那么⾸先需要将 ⾮wav格式 的⾳频⽂件转换为 wav 格式。

 

另外:使⽤ QSound 类时,需要添加模块:multimedia

它的核心API就一个:
 

 

先准备一个.wav格式的音频,然后可以用qrc管理起来。

示例:

#include <QSound> //添加⾳频头⽂件
Widget::Widget(QWidget *parent)
 : QWidget(parent)
 , ui(new Ui::Widget)
{
 ui->setupUi(this);
 QSound *sound = new QSound(":/1.wav",this); //实例化对象
 connect(ui->btn,&QPushButton::clicked,[=](){
 
 sound->play(); //播放
 
 });
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2249077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

永久免费使用最好的Markdown编辑器——Typora

介绍 Typora 是一款轻量级的 Markdown 编辑器&#xff0c;其最为出众的特点是&#xff1a; 所见即所得。 Typora 于2021年11月23日推出了第一个正式版&#xff0c;并转为收费。不过价格也算合理&#xff0c;89元/3台设备&#xff0c;为一次买断制。 直到2022年年中&#xff0…

Linux环境实现c语言编程

Linux环境实现c语言编程 准备工作 准备一台Linux虚拟机/机器 操作流程 打开Linux机器 打开终端 查看是否有GCC编译器 GCC是什么 GCC是GNU编译器集合&#xff08;GNU Compiler Collection&#xff09;的简称。它是一套免费的开源编程语言编译器&#xff0c;支持多种编程语…

VTK中对于相机camera的设置

1. 相机的核心属性 在 VTK 中&#xff0c;vtkCamera 的核心属性有默认值。如果你不设置这些属性&#xff0c;相机会使用默认值来渲染场景。 Position&#xff08;默认值&#xff1a;(0, 0, 1)&#xff09;&#xff1a; 默认情况下&#xff0c;相机位于 Z 轴正方向的 (0, 0, 1)…

glog在vs2022 hello world中使用

准备工作 设置dns为阿里云dns 223.5.5.5&#xff0c;下载cmake&#xff0c;vs2022&#xff0c;git git clone https://github.com/google/glog.git cd glog mkdir build cd build cmake .. 拷贝文件 新建hello world并设置 设置预处理器增加GLOG_USE_GLOG_EXPORT;GLOG_NO_AB…

Python开发环境搭建+conda管理环境

下载Miniconda 推荐从清华镜像下载安装包 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 打开网页后&#xff0c;下拉到最后找到Miniconda3-latest前缀的文件&#xff0c;或者网页中直接搜索Miniconda3-latest&#xff0c;都可以找…

深度学习:GPT-2的MindSpore实践

GPT-2简介 GPT-2是一个由OpenAI于2019年提出的自回归语言模型。与GPT-1相比&#xff0c;仍基于Transformer Decoder架构&#xff0c;但是做出了一定改进。 模型规格上&#xff1a; GPT-1有117M参数&#xff0c;为下游微调任务提供预训练模型。 GPT-2显著增加了模型规模&…

C++设计模式-策略模式-StrategyMethod

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算法都编码到对象中&#xff0c;将会使对象变得异常复杂&#xff1b;而且有时候支持不使用的算法也是一个性能负担。 如何在运…

Harbor安装、HTTPS配置、修改端口后不可访问?

Harbor安装、HTTPS配置、修改端口后不可访问&#xff1f; 大家好&#xff0c;我是秋意零。今天分享Harbor相关内容&#xff0c;安装部分可完全参考官方文档&#xff0c;写的也比较详细。 安装Harbor 官方文档&#xff1a;https://goharbor.io/docs/2.12.0/install-config/ …

DMS2024|思腾合力受邀参加第二届CCF数字医学大会

随着人工智能技术的不断进步&#xff0c;其在医学领域的应用日益广泛。从医学影像分析、疾病诊断到个性化治疗方案设计&#xff0c;人工智能正在逐步改变传统的医疗模式。未来&#xff0c;数字医学将更加注重数据的整合与挖掘&#xff0c;推动医学研究的深入与创新。 2024年11…

Python 绘制 向量减法

Python 绘制 向量减法 flyfish import matplotlib.pyplot as plt# 向量数据 a [1, 2] b [3, 2]# 计算-a 和 a-b minus_b [-x for x in b] # 反转向量b得到-b a_minus_b [a[i] minus_b[i] for i in range(2)] # 计算a - b# 绘制原点 plt.plot([0], [0], ko) # 黑色圆点…

工作坊报名|使用 TEN 与 Azure,探索你的多模态交互新场景

GPT-4o Realtime API 发布&#xff0c;语音 AI 技术正在进入一场新的爆发。语音AI技术的实时语音和视觉互动能力将为我们带来更多全新创意和应用场景。 实时音频交互&#xff1a; 允许应用程序实时接收并响应语音和文本输入。自然语音生成&#xff1a; 减少 AI 技术生成的语音…

node.js基础学习-http模块-创建HTTP服务器、客户端(一)

http模块式Node.js内置的模块&#xff0c;用于创建和管理HTTP服务器。Node.js使用JavaScript实现&#xff0c;因此性能更好。 使用http模块创建服务器&#xff0c;我们建议使用commonjs模块规范&#xff0c;因为很多第三方的组件都使用了这种规范。当然es6写法也支持。 下面就是…

黑马程序员Java项目实战《苍穹外卖》Day01

苍穹外卖-day01 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示&#xff1a; ​ 管理端-外卖商家使用 ​ 用户端-点餐用户使用 当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一…

【NOIP普及组】表达式求值

【NOIP普及组】表达式求值 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给定一个只包含加法和乘法的算术表达式&#xff0c;请你编程计算表达式的值。 输入 输入仅有一行&#xff0c;为需要你计算的…

“蜀道山”高校联合公益赛 Web (部分)

文章目录 奶龙牌WAF海关警察训练平台恶意代码检测器 奶龙牌WAF <?php if ($_SERVER[REQUEST_METHOD] POST && isset($_FILES[upload_file])) {$file $_FILES[upload_file];if ($file[error] UPLOAD_ERR_OK) {$name isset($_GET[name]) ? $_GET[name] : basen…

鸿蒙中的Image组件如何引用网络图片

1.引用网络图片资源 引入网络图片需要申请权限ohos.permission.INTERNET&#xff0c;此时&#xff0c;Image组件的src参数为网络图片的链接&#xff0c;为了成功加载网络图片&#xff0c;您需要在module.json5文件中申请网络访问权限 注意&#xff1a;实际可用的时候&#xff0…

问题记录-Java后端

问题记录 目录 问题记录1.多数据源使用事务注意事项&#xff1f;2.mybatis执行MySQL的存储过程&#xff1f;3.springBoot加载不到nacos配置中心的配置问题4.服务器产生大量close_wait情况 1.多数据源使用事务注意事项&#xff1f; 问题&#xff1a;在springBoot项目中多表处理数…

PySide6 QSS(Qt Style Sheets) Reference: PySide6 QSS参考指南

Qt官网参考资料&#xff1a; QSS介绍&#xff1a; Styling the Widgets Application - Qt for Pythonhttps://doc.qt.io/qtforpython-6/tutorials/basictutorial/widgetstyling.html#tutorial-widgetstyling QSS 参考手册&#xff1a; Qt Style Sheets Reference | Qt Widge…

vue3 开发利器——unplugin-auto-import

这玩意儿是干啥的&#xff1f; 还记得 Vue 3 的组合式 API 语法吗&#xff1f;如果有印象&#xff0c;那你肯定对以下代码有着刻入 DNA 般的熟悉&#xff1a; 刚开始写觉得没什么&#xff0c;但是后来渐渐发现&#xff0c;这玩意儿几乎每个页面都有啊&#xff01; 每次都要写…

搭建AI知识库:打造坚实的团队知识堡垒

在信息爆炸的时代&#xff0c;企业面临着知识管理的挑战。团队知识堡垒的构建&#xff0c;即搭建一个高效的AI知识库&#xff0c;对于保护和利用知识资产、提升团队协作效率和创新能力至关重要。本文将探讨搭建AI知识库的重要性、策略以及如何通过这一系统打造坚实的团队知识堡…