【Qt】系统相关——多线程、Qt多线程介绍、常用函数、线程安全、网络、UDP Socket、TCP Socket

ops/2024/10/31 22:27:59/

文章目录

  • Qt
    • 系统相关
      • 1. 多线程
        • 1.1 Qt多线程介绍
        • 1.2 常用函数
        • 1.3 线程安全
      • 2. 网络
        • 2.1 UDP Socket
        • 2.2 TCP Socket

Qt

在这里插入图片描述
  

系统相关

1. 多线程

1.1 Qt多线程介绍

  QThread 代表一个在应用程序中可以独立控制的线程,它还可以和进程中的其他线程共享数据。QThread 对象管理程序中的一个控制线程。

  

1.2 常用函数
函数 / 信号名功能描述
run()线程的入口函数,包含线程执行的代码逻辑
start()调用 run () 开始执行线程,根据优先级调度,若线程已运行则不做任何事
currentThread()返回指向管理当前执行线程的 QThread 指针
isRunning()线程正在运行则返回 true,否则返回 false
sleep()使线程休眠,单位为秒
msleep()使线程休眠,单位为毫秒
usleep()使线程休眠,单位为微秒
wait()阻塞线程,直到关联线程完成执行(从 run () 返回,若已完成或未启动则返回 true)或达到指定时间(超时返回 false,默认 ULONG_MAX 表示永远等待),类似 POSIX pthread_join () 功能
terminate()终止线程执行,是否立即终止取决于操作系统调度策略,之后建议用 wait ()
finished()线程结束时发出此信号,可用于线程清理工作

  

timethread.h

#ifndef TIMETHREAD_H
#define TIMETHREAD_H#include <QThread>class TimeThread : public QThread
{Q_OBJECTpublic:TimeThread();void run(); //线程任务函数signals:void sendTime(QString Time); //声明信号函数
};#endif // TIMETHREAD_H

  

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <timethread.h> //添加头文件
#include <QLabel>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void on_btn_clicked();void showTime(QString Time);private:QLabel* label;Ui::Widget *ui;TimeThread t; //定义线程对象
};
#endif // WIDGET_H

  

timethread.cpp

#include "timethread.h"#include <QTime>
#include <QDebug>
#include <QString>TimeThread::TimeThread() {}void TimeThread::run()
{while(1){QString time = QTime::currentTime().toString("hh:mm:ss");qDebug() << time;emit sendTime(time); //发送信号sleep(1);}
}

  

widget.cpp

#include "widget.h"
#include "ui_widget.h"#include "timethread.h"
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QSpacerItem>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QGridLayout* layout=new QGridLayout();label=new QLabel(this);label->setFixedSize(500,300);label->setFrameShape(QFrame::Box);label->setText("SSSSSSSSSSSSSSSSSSSS");QPushButton* btn=new QPushButton(this);btn->setFixedSize(100,50);btn->setText("确定");layout->addWidget(label,0,0,1,1);layout->addWidget(btn,1,0,1,1);this->setLayout(layout);connect(&t,&TimeThread::sendTime,this,&Widget::showTime);connect(btn,&QPushButton::clicked,this,&Widget::on_btn_clicked);
}Widget::~Widget()
{delete ui;
}void Widget::on_btn_clicked()
{t.start(); //开启线程
}void Widget::showTime(QString Time)
{label->setText(Time);
}

  

在这里插入图片描述

  
在这里插入图片描述

  

连接类型说明适用场景注意事项
Qt::AutoConnection根据信号和槽所在线程自动选择连接类型,同线程用 Qt::DirectConnection,不同线程用 Qt::QueuedConnection通用场景
Qt::DirectConnection信号发出时,槽函数立即在同线程执行信号和槽在同一线程注意线程安全性
Qt::QueuedConnection信号发出时,槽函数插入接收对象所属线程事件队列,下次事件循环执行信号和槽在不同线程确保线程安全
Qt::BlockingQueuedConnection与 Qt::QueuedConnection 类似,但发送信号线程会阻塞至槽函数执行完毕需等待槽函数执行完再继续的场景注意可能引起线程死锁
Qt::UniqueConnection可与其他连接类型位或组合使用用于保证相同信号和槽之间只有一个连接

  

1.3 线程安全

  实现线程互斥和同步常用的类有:
  • 互斥锁:QMutex、QMutexLocker
  • 条件变量:QWaitCondition
  • 信号量:QSemaphore
  • 读写锁:QReadLocker、QWriteLocker、QReadWriteLock

  

QMutex(互斥锁)

函数功能描述
QMutex()创建互斥锁。
~QMutex()销毁互斥锁。
lock()获取锁,阻塞等待。
tryLock()尝试获取锁,不阻塞。
unlock()释放锁。
recursiveLock()(递归锁)获取递归锁。
tryRecursiveLock()尝试获取递归锁。
unlockRecursive()释放递归锁。

  

QMutexLocker(互斥锁辅助类)

函数功能描述
QMutexLocker(QMutex * mutex)构造时尝试获取互斥锁。
~QMutexLocker()析构时释放互斥锁。
relock()重新获取互斥锁。
unlock()手动释放互斥锁。

  

QWaitCondition(条件变量)

函数功能描述
QWaitCondition()创建条件变量。
~QWaitCondition()销毁条件变量。
wait(QMutex * mutex)线程等待,释放互斥锁,被唤醒后重新获取。
wait(QMutex * mutex, ulong time)等待指定时间,超时返回 false。
wakeOne()唤醒一个等待线程。
wakeAll()唤醒所有等待线程。

  

QSemaphore(信号量)

函数功能描述
QSemaphore(int n = 0)创建信号量,设置初始值。
~QSemaphore()销毁信号量。
acquire(int n = 1)获取 n 个资源,阻塞等待。
tryAcquire(int n = 1)尝试获取 n 个资源,不阻塞。
release(int n = 1)释放 n 个资源。

  

QReadLocker(读写锁 - 读操作辅助类)

函数功能描述
QReadLocker(QReadWriteLock * lock)构造时获取读锁。
~QReadLocker()析构时释放读锁。
relock()重新获取读锁。
unlock()手动释放读锁。

  

QWriteLocker(读写锁 - 写操作辅助类)

函数功能描述
QWriteLocker(QReadWriteLock * lock)构造时获取写锁。
~QWriteLocker()析构时释放写锁。
relock()重新获取写锁。
unlock()手动释放写锁。

  

QReadWriteLock(读写锁)

函数功能描述
QReadWriteLock()创建读写锁。
~QReadWriteLock()销毁读写锁。
lockForRead()获取读锁,阻塞。
tryLockForRead()尝试获取读锁,不阻塞。
unlock()释放锁。
lockForWrite()获取写锁,阻塞。
tryLockForWrite()尝试获取写锁,不阻塞。

  

2. 网络

2.1 UDP Socket

QUdpSocket

名称类型说明对标原生 API
bind(const QHostAddress&, quint16)方法绑定指定的端口号bind
receiveDatagram()方法返回 QNetworkDatagram,读取一个 UDP 数据报recvfrom
writeDatagram(const QNetworkDatagram&)方法发送一个 UDP 数据报sendto
readyRead信号在收到数据并准备就绪后触发无(类似 IO 多路复用的通知机制)

  

QNetworkDatagram

名称类型说明对标原生 API
QNetworkDatagram(const QByteArray&, const QHostAddress&, quint16)构造函数通过 QByteArray、目标 IP 地址、目标端口号构造一个 UDP 数据报,通常用于发送数据时
data()方法获取数据报内部持有的数据,返回 QByteArray
senderAddress()方法获取数据报中包含的对端的 IP 地址无(recvfrom 包含该功能)
senderPort()方法获取数据报中包含的对端的端无(recvfrom 包含该功能)

  

服务端:
  
widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();QUdpSocket* socket;void processRequest();QString process(const QString&);private:Ui::Widget *ui;
};
#endif // WIDGET_H

  

widget.cpp

#include "widget.h"
#include "ui_widget.h"#include <QMessageBox>
#include <QUdpSocket>
#include <QNetworkDatagram>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 设置窗⼝标题this->setWindowTitle("服务器");// 2. 实例化 socketsocket = 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;}
}Widget::~Widget()
{delete ui;
}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;
}

  

客户端:
  
widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;// 创建 socket 成员QUdpSocket* socket;
};
#endif // WIDGET_H

  

widget.cpp

#include "widget.h"
#include "ui_widget.h"#include <QNetworkDatagram>// 提前定义好服务器的 IP 和 端⼝
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 设置窗口名字this->setWindowTitle("客户端");// 2. 实例化 socketsocket = new QUdpSocket(this);connect(socket, &QUdpSocket::readyRead, this, [=]() {const QNetworkDatagram responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();ui->listWidget->addItem(QString("服务器说: ") + response);});
}Widget::~Widget()
{delete ui;
}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 Socket

QTcpServer

名称类型说明对标原生 API
listen(const QHostAddress&, quint16 port)方法绑定指定地址和端口号并开始监听bind 和 listen
nextPendingConnection()方法从系统获取已建立好的 tcp 连接,返回 QTcpSocket 用于和客户端通信accept
newConnection信号新客户端连接建立好后触发无(类似 IO 多路复用通知机制)

  

QTcpSocket

名称类型说明对标原生 API
readAll()方法读取接收缓冲区所有数据,返回 QByteArrayread
write(const QByteArray&)方法将数据写入 socketwrite
deleteLater方法标记 socket 对象无效,在下个事件循环析构释放无(类似 “半自动垃圾回收”)
readyRead信号数据到达且准备就绪时触发无(类似 IO 多路复用通知机制)
disconnected信号连接断开时触发无(类似 IO 多路复用通知机制)

            


http://www.ppmy.cn/ops/129953.html

相关文章

无人机避障——路径规划篇(一) JPS跳点搜索算法A*算法对比

JSP 跳点搜索算法与改进 A*算法对比 一、算法概述: 跳点搜索(Jump Point Search,JPS)算法:一种用于路径规划的启发式搜索算法。它主要用于在网格地图(如游戏地图、机器人运动规划地图等)中快速找到从起点到终点的最短路径。该算法在改进 A*算法的基础上进行了优化,通过跳过一…

【在Linux世界中追寻伟大的One Piece】Socket编程TCP

目录 1 -> TCP socket API 2 -> V1 -Echo Server 2.1 -> 测试多个连接的情况 1 -> TCP socket API socket()&#xff1a; socket()打开一个网络通讯端口&#xff0c;如果成功的话&#xff0c;就像open()一样返回一个文件描述符。应用程序可以像读写文件一样用r…

一个vue3的待办列表组件

一个vue3的待办列表组件, 仿企业微信的待办列表 TodoList.vue <template><div><el-input v-model"todoInput" placeholder"写下你的待办事项..." class"el-input" keyup.enter"addTodo"input-style"background-c…

Chromium HTML Input 类型password 对应c++

一、密码字段&#xff1a; 密码字段通过标签 <input type"password"> 来定义: <form> Password: <input type"password" name"pwd"> </form> 二、password 对应c定义&#xff1a; third_party\blink\renderer\core\…

VR动捕数据手套如何配合头显装置进行机器臂遥操作?

早期的遥操作机器人主要通过直接控制结构实现&#xff0c;用户通过主机通讯接口直接控制机器人的运动。然而&#xff0c;随着应用场景的复杂化&#xff0c;直接控制结构的局限性逐渐显现。进入21世纪&#xff0c;随着5G、AI和VR等新兴技术的融入&#xff0c;遥操作机器人变得更…

记录一个跳跃的小游戏

记录一个跳跃的小游戏 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevic…

Lua语法基础全面剖析(中篇)

接上篇&#xff0c; 今天我们来进行Lua语法全面剖析。 由于篇幅过长&#xff0c;将分为上中下三篇进行讲解&#xff0c;本篇为中篇。 6.字符串 6.1 string 类型变量 字符串&#xff08;即string&#xff09;&#xff0c;就是一串文本数据&#xff0c;可以存储你要的文本。 …

前端代码注释

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言类注释属性注释函数注释函数参数注释解构 & 函数返回结果 注释Vue Props 注释注释建议注释内容要清晰简洁注释类型避免不必要的注释采用一致的风格版本与更…