当前位置: 首页 > news >正文

QT系统篇(5)(下)

一、多线程

1.了解

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

2.方法

3.方法

wait

bool QThread::wait( unsigned long time = ULONG_MAX ); // 功能: // 阻塞当前线程,直到该 QThread 对象所代表的线程执行完毕(退出),或达到指定的超时时间 // 参数说明: // unsigned long time = ULONG_MAX: // 等待的超时时间(单位:毫秒) // - 若 time == ULONG_MAX(默认):无限等待,直到目标线程退出 // - 若 time > 0:最多等待 time 毫秒,超时后返回 false // 返回值: // bool: // - 若目标线程已正常退出,则返回 true // - 若超时(time 指定的毫秒数内线程未退出),则返回 false // - 注意:如果目标线程尚未启动(未调用 start())或已经退出,调用 wait() 也会立即返回 true

4.代码

//thread.h class Thread : public QThread { Q_OBJECT public: Thread(); void run();//重写run函数 public: static int num;//统计数量 static QMutex mutex; }; //thread.cpp int Thread::num = 0; QMutex Thread::mutex; Thread::Thread() {} void Thread::run() { for(int i = 0;i < 10;i++) { // mutex.lock();//上锁 // num++; // mutex.unlock();//解锁 QMutexLocker locker(&mutex);//上锁,当这个对象销毁后会自动解锁 num++; } } //.cpp Thread t1; Thread t2; //启动线程 t1.start(); t2.start(); //阻塞主线程,等待t1线程和t2线程返回 t1.wait(); t2.wait(); qDebug()<<Thread::num;

5.要点

  • QMutexLocker是 Qt 中的智能锁,用于自动管理互斥量的加锁和解锁。

  • Qt使用多线程的意义在于,将IO 操作隔离出去,即保证主线程与上传/下载等操作隔离。例如游戏一边更新还能一边运行,就是因为更新(IO 操作)放在后台线程,主线程仍可处理界面和交互。

  • 服务器使用多线程,是为了充分利用 CPU 资源(避免单线程下 I/O 等待导致的 CPU 空闲)。

  • Qt中,工作线程不允许直接操作UI界面,否则会报错;而是通过信号槽的方式(将数据发送至主线程)间接操作UI。

6.connect拓展

connect()函数的第五个参数为Qt::ConnectionType,用于指定信号和槽的连接类型,同时影响信号的传递方式和槽函数的执行顺序。Qt::ConnectionType提供了以下五种方式

1.1 线程安全

1.互斥锁的种类

QMutex、QMutexLocker

2.互斥锁的作用

互斥锁是一种保护和防止多个线程同时访问同一对象实例的方法。在 Qt 中,互斥锁主要是通过QMutex类来处理。

3.QMutex和QMutexLocker的区别

QMutex

  • 特点QMutex是 Qt 框架提供的互斥锁类,用于保护共享资源的访问,实现线程间的互斥操作。

  • 用途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全

QMutex mutex; mutex.lock(); //上锁 //访问共享资源 //... mutex.unlock(); //解锁

QMutexLocker

  • 特点QMutexLocker是 QMutex 的辅助类,使用RAII(Resource Acquisition Is Initialization)方式对互斥锁进行加锁解锁操作。

  • 用途:简化对互斥锁的加锁和解锁操作,避免忘记解锁导致的死锁等问题。

QMutex mutex; { QMutexLocker locker(&mutex); //在作⽤域内⾃动上锁 //访问共享资源 //... }//在作⽤域结束时⾃动解锁

4.其他锁

  1. QReadLocker(共享锁)

用于读操作上锁,允许多个线程同时读取共享资源,但不能写入

  1. QWriteLocker(独占锁,和正常锁没区别)

用于写操作上锁,只允许一个线程写入共享资源,并且禁止其他线程读取或写入

1.2 条件变量

1.了解

在多线程编程中,除了需要等待操作系统调度之外,有时一个线程还必须等待某个特定条件满足才能继续执行,这就带来了同步问题。通常的解决思路是:该线程先释放互斥锁(或读写锁),然后进入睡眠状态,让其他线程得以运行。当条件满足时,另一个线程会将其唤醒

在 Qt 中,专门提供了QWaitCondition类来解决像上述这样的问题。

  • 特点QWaitCondition是 Qt 框架提供的条件变量类,用于线程之间的消息通信和同步。

  • 用途:在某个条件满足时等待或唤醒线程,用于线程的同步和协调。

2.代码

//.h class MyWorker : public QThread { Q_OBJECT public: explicit MyWorker(QObject *parent = nullptr); // 由主线程调用,用于唤醒等待中的工作线程 void trigger(); // 线程入口函数,在其中等待条件满足 void run(); private: QMutex m_mutex;//锁 QWaitCondition m_cond;//条件变量 bool m_ready;// 条件标志 }; //.cpp void MyWorker::run() { qDebug() << "Worker: 等待条件..."; m_mutex.lock(); while (!m_ready) { // 条件不满足,线程进入睡眠等待,同时自动释放 m_mutex,唤醒后又会自动申请锁 m_cond.wait(&m_mutex); } m_mutex.unlock();//给没有睡眠的或者唤醒后的进行解锁 qDebug() << "Worker: 条件满足,继续执行"; } void MyWorker::trigger() { m_mutex.lock(); m_ready = true; //改变条件 m_cond.wakeOne(); //唤醒一个等待的线程 m_mutex.unlock(); }

3.要点

  • 线程进入休眠会自动释放锁,唤醒后又会自动重新申请锁,然后从当前位置开始执行代码

  • 使用while而不是if的原因主要有两点:

  1. 虚假唤醒:操作系统可能无故唤醒等待的线程,此时条件并未满足,需要重新等待。

  2. 多线程竞争:当线程被唤醒并重新获取锁之前,其他线程可能已经改变了条件,导致条件再次不满足。

  3. 补充:为什么使用while而不是if?因为如果使用if,被唤醒后肯定if条件不满足,但代码不会执行else ifelse分支(即不会重新检查条件)。此外,还存在一种情况:线程被唤醒后可能没有立即申请到锁(需要继续等待锁到位),而在等待锁的过程中,条件可能再次发生变化。因此必须使用while循环反复检查条件,确保在条件真正满足时才继续执行。

4.方法

wakeOne

#include <QWaitCondition> void QWaitCondition::wakeOne(); // 功能: // 唤醒一个正在等待该 QWaitCondition 的线程 // 如果有多个线程在等待,则随机选择一个线程唤醒 // 被唤醒的线程会重新尝试获取互斥锁(QMutex)并继续执行 // 参数说明: // 无参数 // 返回值: // 无(void)

wakeAll

#include <QWaitCondition> void QWaitCondition::wakeAll(); // 功能: // 唤醒所有正在等待该 QWaitCondition 的线程 // 所有被唤醒的线程会依次尝试重新获取互斥锁(QMutex)并继续执行 // 参数说明: // 无参数 // 返回值: // 无(void)

1.3 信号量

1.了解

有时在多线程编程中,需要确保多个线程能够并发访问数量有限的同一类资源。例如,设备内存有限,我们希望需要大量内存的线程能够根据可用内存数量来决定行为。这类问题通常用信号量来解决。信号量可以看作是增强版的互斥锁,不仅能完成加锁解锁,还能跟踪可用资源的数量

  • 特点QSemaphore是 Qt 提供的计数信号量类,用于控制同时访问共享资源的线程数量

  • 用途:限制并发线程数,解决资源有限的场景。

2.代码

//.h class SemaphoreApiDemo : public QObject { Q_OBJECT public: explicit SemaphoreApiDemo(QObject *parent = nullptr); }; #endif //.cpp SemaphoreApiDemo::SemaphoreApiDemo(QObject *parent) : QObject(parent) { // 1. 创建一个初始计数为 2 的信号量(同时允许两个线程访问资源) QSemaphore semaphore(2); qDebug() << "初始信号量可用计数:" << semaphore.available(); // 2. 第一次 acquire():计数减1,变成 1 semaphore.acquire(); qDebug() << "执行一次 acquire() 后,计数 = " << semaphore.available(); // 3. 第二次 acquire():计数减1,变成 0 semaphore.acquire(); qDebug() << "执行第二次 acquire() 后,计数 = " << semaphore.available(); // 4. 第三次 acquire():此时计数为0,调用会阻塞。 // 为了不卡死程序,我们用 tryAcquire() 展示非阻塞版本(返回 false 而不阻塞)。 bool success = semaphore.tryAcquire(); // 立即返回,不阻塞 qDebug() << "第三次尝试获取(tryAcquire)结果:" << success << ",计数仍为" << semaphore.available(); // 5. 执行 release():释放一个资源,计数从0变回1 semaphore.release(); qDebug() << "执行一次 release() 后,计数 = " << semaphore.available(); }

3.方法

available

#include <QSemaphore> int available() const; // 功能: // 返回当前可用的资源数量 // 参数说明: // 无参数 // 返回值: // int: // 当前可用的资源数量,该值永远不会为负数

semaphore

#include <QSemaphore> QSemaphore::QSemaphore( int n = 0 ); // 功能: // 构造一个信号量,并初始化可用的资源数量 // 参数说明: // int n = 0: // 信号量初始可用的资源数量(不可为负数,默认为 0) // 返回值: // 无(void)

acquire

#include <QSemaphore> void QSemaphore::acquire( int n = 1 ); // 功能: // 尝试获取 n 个资源。如果没有足够的资源可用,调用将阻塞直到资源可用 // 参数说明: // int n = 1: // 需要获取的资源数量(默认为 1) // 返回值: // 无(void)

release

#include <QSemaphore> void QSemaphore::release( int n = 1 ); // 功能: // 释放 n 个资源,使可用资源数量增加 // 参数说明: // int n = 1: // 需要释放的资源数量(默认为 1) // 返回值: // 无(void)

tryAcquire

#include <QSemaphore> // bool QSemaphore::tryAcquire( // int n = 1 // ); // 功能: // 尝试获取 n 个资源,不会阻塞,而是立即返回 // 参数说明: // int n = 1: // 需要尝试获取的资源数量(默认为 1) // 返回值: // bool: // - 若成功获取了 n 个资源,返回 true // - 若当前可用资源不足,返回 false,且不会获取任何资源

二、Qt网络

1.提要

在进⾏⽹络编程之前, 需要在项⽬中的 .pro ⽂件中添加 network 模块

2.1 UDP

1.QUdpSocket(socket文件)

2.QNetworkDategram(UDP数据报数据)

3.代码

服务器

//.h private slots: void ProcessRequest();//处理函数 private: QUdpSocket* socket;//创建对象 //.cpp //1.设置窗口标题 this->setWindowTitle("服务器"); //2.实例化socket socket = new QUdpSocket(this); //3.连接信号槽,处理收到的请求 connect(socket,&QUdpSocket::readyRead,this,&Widget::ProcessRequest); //4.绑定端口 bool ret = socket->bind(QHostAddress::Any,9090); if(!ret) { QMessageBox::critical(nullptr, "服务器启动出错", socket->errorString()); return; } void Widget::ProcessRequest() { //1.读取数据 const QNetworkDatagram& requestDatagram = socket->receiveDatagram(); QString request = requestDatagram.data(); //2.对数据进行处理 const QString& response= Process(request); //3.把响应结果写到客户端 QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(), requestDatagram.senderPort()); socket->writeDatagram(responseDatagram); // 显⽰打印⽇志 QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort()) + "] req: " + request + ", resp: " + response; ui->listWidget->addItem(log); } QString Widget::Process(const QString request) { return request; }

客户端

//.h QUdpSocket* socket; //.cpp //1.设置窗口名字 this->setWindowTitle("客户端"); //2.实例化socket socket = new QUdpSocket(this); //用于显示回响到客户端 // connect(socket,&QUdpSocket::readyRead,this,[=](){ // const QNetworkDatagram responDatagram = socket->receiveDatagram(); // QString response = responDatagram.data(); // ui->listWidget->addItem(QString("服务器说:")+response); // }); } void Widget::on_pushButton_clicked() { //1.获取输入框的内容 const QString& text = ui->lineEdit->text(); //2.构建请求数据 QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT); //3.发送请求 socket->writeDatagram(requestDatagram); //4.将消息添加到列表框 ui->listWidget->addItem("客户端说:" + text); //5.清空输入框 ui->lineEdit->setText(""); }

2.2 TCP

1.QTcpServer(用于监听端口,获取客户端连接)

2.QTcpSocket(用户端和服务器之间的数据交互)

3.代码

服务器

//.h public: QString Process(const QString&);//回响函数 private slots: void ProcessConnection();//处理流程 private: QTcpServer* tcpServer;//创建QTcpServer //.cpp //1.设置窗口标题 this->setWindowTitle("服务器"); //2.实例化TcpServer tcpServer = new QTcpServer(this); //3.通过信号槽,处理客户端建立的连接 connect(tcpServer,&QTcpServer::newConnection,this,&Widget::ProcessConnection); //4.监听端口 bool ret = tcpServer->listen(QHostAddress::Any,9090); if(!ret) { qDebug() << "服务器启动失败" << tcpServer->serverError(); exit(1); } void Widget::ProcessConnection() { //1.获取新连接的对应的socket QTcpSocket* clientSocket = tcpServer->nextPendingConnection(); QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端上线!"; ui->listWidget->addItem(log); //2.通过信号槽,处理收到的请求情况 connect(clientSocket,&QTcpSocket::readyRead,this,[=]() { //读取 QString request = clientSocket->readAll(); //根据请求处理响应 const QString& response = Process(request); //把响应写回客户端 clientSocket->write(response.toUtf8()); QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] req: " + request + ", resp: " + response; ui->listWidget->addItem(log); }); //3.通过信号槽,处理断开连接的情况 connect(clientSocket, &QTcpSocket::disconnected, this, [=]() { QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端下线!"; ui->listWidget->addItem(log); // 删除 clientSocket clientSocket->deleteLater();//等待当前槽函数及所有关联事件处理完毕后,由事件循环自动销毁 }); } QString Widget::Process(const QString &request) { return request; }

客户端

//.h private: QTcpSocket* tcpClient; //.cpp // 1. 设置窗⼝标题. this->setWindowTitle("客⼾端"); // 2. 实例化 socket 对象. tcpClient = new QTcpSocket(this); // 3. 和服务器建⽴连接. tcpClient->connectToHost(QHostAddress("127.0.0.1"), 9090); // 4. 等待并确认连接是否出错. if(!tcpClient->waitForConnected()) { qDebug()<<"连接服务器出场:" << tcpClient->errorString(); exit(1); } void Widget::on_pushButton_clicked() { //获取输入框内容 const QString& text = ui->lineEdit->text(); //把消息显示到界面上 ui->listWidget->addItem(QString("客户端说:") + text); //发送给服务器 tcpClient->write(text.toUtf8()); //情况输入框内容 ui->lineEdit->setText(""); }

2.3 HTTP

1.方法

2.代码

//.h private slots: void on_pushButton_clicked(); private: QNetworkAccessManager* manager; //.cpp manager = new QNetworkAccessManager(this); void Widget::on_pushButton_clicked() { //1.获取输入框的URL,构造QUrl对象 QUrl url(ui->lineEdit->text()); //2.构造HTTP请求对象(1) QNetworkRequest request(url); //3.发送GET请求(2) QNetworkReply* response = manager->get(request); // 4. 通过信号槽来处理响应 connect(response, &QNetworkReply::finished, this, [=]() { if (response->error() == QNetworkReply::NoError) { // 响应正确 QString html(response->readAll()); ui->plainTextEdit->setPlainText(html); } else { // 响应出错 ui->plainTextEdit->setPlainText(response->errorString()); } response->deleteLater(); }); }
http://www.gsyq.cn/news/1635806.html

相关文章:

  • 网盘下载慢到抓狂?这个开源浏览器脚本让你轻松获取高速直链
  • 机械工程论文降AI工具免费推荐:2026年机械工程毕业论文降AI4.8元知网达标完整方案
  • 架构评审数据化:别让评审会只剩观点碰撞
  • NVIDIA Profile Inspector:解锁显卡隐藏性能,让你的游戏体验飞起来
  • 华硕笔记本轻量级控制中心:释放硬件潜力的终极解决方案
  • GDSDecomp技术实现:PCK文件极速修改与Godot逆向工程架构设计
  • 自己写一个《英雄无敌3》战斗AI
  • 免费分享最新IDEA安装及授权教程(附带文件)
  • 终极指南:40+经典DSGE模型库如何加速你的宏观经济研究
  • FigmaCN:5分钟快速汉化Figma界面,中文设计师的完整解决方案
  • GTA5终极修改器YimMenu:10分钟快速上手指南
  • 独立开发实战:学生管理+考试防作弊机制设计
  • 深耕低代码5年,终于遇见打破行业桎梏的AI原生平台
  • 不受待见的钻石又火了?新娘不要英伟达为啥抢着要?
  • OpenClaw:AI智能体开发的高效跨平台解决方案
  • Python 语法基础 IO
  • 做网课直播还在用手比划?这两款键盘鼠标显示工具,让观众看清你的每一步操作
  • HoRain云--Java文档注释规范与最佳实践指南
  • Java多态:一个父类引用,搞定千变万化的子类
  • 堆与优先队列的并发安全实现机制的技术7
  • 【花雕动手做】行空板 K10 系列实验之TT马达双路差速智能小车方案三号特色底盘
  • 2025微信小程序反编译终极指南:如何用unveilr快速提取小程序源码
  • 黑苹果配置革命:OpCore Simplify - 自动化EFI生成终极解决方案
  • Deepin Boot Maker终极指南:3步制作Linux启动盘的最佳实践
  • 林伽一 · AI科技日报 |LongCat-2.0宣称中国芯片突破,Claude Sonnet 5自报分数解析
  • OpenCore Legacy Patcher完整指南:让老款Mac免费升级最新macOS的终极方案
  • 用 AI 写代码做家庭调酒小程序:真正难的是把酒库到保存跑通
  • YOLOv13超图视觉与NCNN部署实战指南
  • 【OpenHarmony/HarmonyOs 】实验室首页细节拆解:分类侧栏、搜索筛选与推荐探索交互
  • python___模块