虽然Qt不是跨平台的C++开发框架,Qt的很多能力其实是操作系统提供的,只不过Qt封装了系统的API,程序是运行在操作系统上的,需要系统提供给我们支撑。信号槽中,用户进行的各种操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动得执行到对应得槽函数,事件也非常类似,用户进行得各项操作,也会产生事件,程序员同样可以给事件关联上处理函数,当事件触发得时候,就能执行对应得代码,事件本身是操作系统提供得机制,Qt也同样把操作系统事件机制进行了封装拿到Qt中,但由于对于事件得代码写起来不太方便,于是Qt就又进行了封装才有的信号槽,所以信号槽是事件机制得进一步封装,而信号槽得底层机制就是事件机制。信号槽搞不定得就需要重写事件处理函数来手动实现事件处理得响应逻辑
Qt 事件
事件介绍
事件是应用程序内部或者外部产生的事情或者动作的统称。在 Qt 中使用一个对象来表示一个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件是在用户操作时发出,如键盘事件、鼠标事件等,另一些事件则是由系统本身自动发出,如定时器事件。常见的 Qt 事件如下:

|----------|---------------------------------|
| 事件名称 | 描述 |
| 鼠标事件 | 鼠标左键、鼠标右键、鼠标滚轮,鼠标的移动,鼠标按键的按下和松开 |
| 键盘事件 | 按键类型、按键按下、按键松开 |
| 定时器事件 | 定时时间到达 |
| 进入离开事件 | 鼠标的进入和离开 |
| 滚轮事件 | 鼠标滚轮滚动 |
| 绘屏事件 | 重绘屏幕的某些部分 |
| 显示隐藏事件 | 窗口的显示和隐藏 |
| 移动事件 | 窗口位置的变化 |
| 窗口事件 | 是否为当前窗口 |
| 大小改变事件 | 窗口大小改变 |
| 焦点事件 | 键盘焦点移动 |
| 拖拽事件 | 用鼠标进行拖拽 |
cpp
#include "label.h"
#include<QDebug>
Label::Label(QWidget* parent):QLabel( parent)
{
}
void Label::enterEvent(QEvent *event)
{
qDebug()<<"eventEvent";
}
void Label::leaveEvent(QEvent *event)
{
qDebug()<<"leaveEvent";
}
自己写一个类,然后继承QLabel,重写Label的leaveEvent和enterEvent,然后得把该label在图像化界面提升成自己得类,因为图形化界面里的默认是QLabel,我们是写了一个子类来继承QLabel,里面才有实现得逻辑


头文件和类名等要和我们创建子类时设置得一样才行
鼠标按下事件
mousePressEvent这个函数,按下左键,右键,滚轮等鼠标按键都能触发
cpp
#include "label.h"
#include<QDebug>
#include<QMouseEvent>
Label::Label(QWidget* parent):QLabel(parent)
{
}
void Label::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug()<<"按下左键";
}
else if(event->button() == Qt::RightButton)
{
qDebug()<<"按下右键";
}
//当前event对象就包含了鼠标点击位置得坐标
//相对于控件的位置
qDebug()<<event->x()<<", "<<event->y();
//相对整个屏幕
qDebug()<<event->globalX()<<", "<<event->globalY();
}

鼠标释放事件
cpp
void Label::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug()<<"左键释放";
}
else if(event->button() == Qt::RightButton)
{
qDebug()<<"释放右键";
}
}
双击事件
cpp
void Label::mouseDoubleClickEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug()<<"双击左键";
}
else if(event->button() == Qt::RightButton)
{
qDebug()<<"双击右键";
}
鼠标移动事件
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QMouseEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//Qt默认不对鼠标移动响应,因为负载太大,要实现得话得手动打开
this->setMouseTracking(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
qDebug()<<event->x()<<event->y();
}
滚轮事件
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QWheelEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
total = 0 ;
}
Widget::~Widget()
{
delete ui;
}
void Widget::WheelEvent(QWheelEvent *event)
{
total+=event->delta();
qDebug()<<total;
}
键盘事件 QShortCut
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QKeyEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier)
{
qDebug()<<"按下了ctrl + a 键";
}
else if(event->key() == Qt::Key_A)
{
qDebug()<< "按下了A键";
}
}

定时器
Qt 中在进行窗口程序的处理过程中,经常要周期性的执行某些操作,或者制作一些动画效果,使用定时器就可以实现。所谓定时器就是在间隔一定时间后,去执行某一个任务。定时器在很多场景下都会使用到,如弹窗自动关闭之类的功能等。Qt中的定时器分为 QTimerEvent 和 QTimer 这2个类。
- QTimerEvent类 用来描述一个定时器事件。在使用时需要通过 startTimer() 函数来开启一个定时器,这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进行相关操作。
- QTimer类 来实现一个定时器,它提供了更高层次的编程接口,如:可以使用信号和槽,还可以设置只运行一次的定时器。
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//开启定时器事件
this->startTimer(1000);
//此处timerId 是一个定时器得身份标识
timerId = this ->startTimer(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::timerEvent(QTimerEvent *event)
{
//如果一次程序中存在多个startTimer定时器,此时每个定时器都会触发timerEvent函数
//所以得先判断一下是哪个定时器触发的
if(event->timerId() != this->timerId)
{
//不是忽略
return;
}
//是
int value = ui->lcdNumber->intValue();
if(value <= 0)
{
//停止
this->killTimer(this->timerId);
return;
}
value-=1;
ui->lcdNumber->display(value);
}
窗口改变事件
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QMoveEvent>
#include<QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::moveEvent(QMoveEvent *event)
{
qDebug()<<event->pos();
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug()<<event->size();
}
Qt 文件
Qt 文件概述
文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。 Qt提供了很多关于文件的类,通过这些类能够对文件系统进行操作,如文件读写、文件信息获取、文件复制或重命名等。
输⼊输出设备类
在 Qt 中,文件读写的类为 QFile 。QFile 的父类为 QFileDevice ,QFileDevice 提供了文件交互操作的底层功能。 QFileDevice 的父类是 QIODevice,QIODevice 的父类为 QObject 。QIODevice 是 Qt 中所有输入输出设备(input/output device,简称 I/O 设备)的基础类,I/O 设备就是能进行数据输入和输出的设备,例如文件是一种 I/O 设备,网络通信中的 socket 是 I/O 设备, 串口、蓝牙等通信接口也是 I/O 设备,所以它们也是从 QIODevice 继承来的。Qt 中主要的一些 I/O 设备
类的继承关系如下图所示:

上图中各类的说明如下:
- QFile 是用于文件操作和文件数据读写的类,使用 QFile 可以读写任意格式的文件。
- QSaveFile 是用于安全保存文件的类。使用 QSaveFile 保存文件时,它会先把数据写入一个临时文件,成功提交后才将数据写入最终的文件。如果保存过程中出现错误,临时文件里的数据不会被写入最终文件,这样就能确保最终文件中不会丢失数据或被写入部分数据。 在保存比较大的文件或复杂格式的文件时可以使用这个类,例如从网络上下载文件等。
- QTemporaryFile 是用于创建临时文件的类。使用函数 QTemporaryFile::open() 就能创建⼀个文件名唯一的临时文件,在 QTemporaryFile 对象被删除时,临时文件被自动删除。
- QTcpSocket 和 QUdpSocket 是分别实现了 TCP 和 UDP 的类。
- QSerialPort 是实现了串口通信的类,通过这个类可以实现计算机与串⼝设备的通信。
- QBluetoothSocket 是用于蓝牙通信的类。手机和平板计算机等移动设备有蓝牙通信模块,笔记本电脑一般也有蓝牙通信模块。通过QBluetoothSocket类,就可以编写蓝牙通信程。如编程实现笔记本电脑与手机的蓝牙通信。
- QProcess 类用于启动外部程序,并且可以给程序传递参数。
- QBuffer 以一个 QByteArray 对象作为数据缓冲区,将 QByteArray 对象当作一个 I/O 设备来读写。
文件读写类
在 Qt 中,文件的读写主要是通过 QFile 类来实现。在 QFile 类中提供了一些用来读写文件的方法。对于文件的操作主要有:
- 读数据:QFile 类中提供了多个方法用于读取文件内容;如 read()、readAll()、readLine()等。
- 写数据:QFile 类中提供了多个方法用于往文件中写内容;如 write()、writeData()等。
- 关闭文件:文件使用结束后必须用函数 close() 关闭文件。
访问一个设备之前,需要使用 open()函数 打开该设备,而且必须指定正确的打开模式,QIODevice 中所有的打开模式由 QIODevice::OpenMode 枚举变量定义,其取值如下:

|-----------------------|-----------------------------------------------------------------|
| QIODevice::NotOpen | 没有打开设备 |
| QIODevice::ReadOnly | 以只读方式打开设备 |
| QIODevice::WriteOnly | 以只写方式打开设备 |
| QIODevice::ReadWrite | 以读写方式打开设备 |
| QIODevice::Append | 以追加方式打开设备,数据将写到文件末尾 |
| QIODevice::Truncate | 每次打开文件后重写文件内容,原内容将被删除 |
| QIODevice::Text | 在读文件时,行尾终止符会被转换为 '\n';当写入文件时,行尾终止符会被转换为本地编码。如 Win32上为'\r\n'; |
| QIODevice::Unbuffered | 无缓冲形式打开文件,绕过设备中的任何缓冲区 |
| QIODevice::NewOnly | 文件存在则打开失败,不存在则创建文件 |
实现一个记事本
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QPlainTextEdit>
#include<QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("记事本");
//获取到菜单栏
QMenuBar * menuBar = this->menuBar();
//添加菜单
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
//添加菜单项
QAction* action1 = new QAction("打开");
QAction* action2 = new QAction("保存");
menu->addAction(action1);
menu->addAction(action2);
//指定一个输入框
edit = new QPlainTextEdit();
QFont font;
font.setPixelSize(20);
edit->setFont(font);
this->setCentralWidget(edit);
//连接Qaction的信号槽
connect(action1,&QAction::triggered,this,&MainWindow::handleAction1);
connect(action2,&QAction::triggered,this,&MainWindow::handleAction2);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::handleAction1()
{
//1.弹出一个打开文件的对话框
QString path = QFileDialog::getOpenFileName(this);
//2.把文件名显示到状态栏
QStatusBar * statubar = this->statusBar();
statubar->showMessage(path);
//3.根据用户选择的路径构造一个QFile对象
QFile file (path);
bool ret = file.open(QIODevice::ReadOnly);
if(!ret)
{
//打开文件失败!
statubar->showMessage(path + "打开失败!");
return;
}
//4.读取文件
QString text = file.readAll();
//5.关闭文件
file.close();
//6.读到了的内容设置到输入框中
edit->setPlainText(text);
}
void MainWindow::handleAction2()
{
//1.弹出一个保存文件的对话框
QString path = QFileDialog::getSaveFileName(this);
//2.在状态栏中显示这个文件名
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path);
//根据用户选择的路径去构造一个QFile对象,并打开
QFile file(path);
bool ret = file.open(QIODevice::WriteOnly);
if (!ret){
statusBar->showMessage(path + "打开失败");
return;
}
//4.写文件
const QString& text = edit->toPlainText();
file.write(text.toUtf8());
//5.关闭文件
file.close();
}

文件和目录信息类
QFileInfo 是 Qt 提供的一个用于获取文件和目录信息的类,如获取文件名、文件大小、文件修改日期等。QFileInfo类中提供了很多的方法,常用的有:
- isDir() 检查该文件是否是目录;
- isExecutable() 检查该文件是否是可执行文件;
- fileName() 获得文件名;
- completeBaseName() 获取完整的文件名;
- suffix() 获取文件后缀名;
- completeSuffix() 获取完整的文件后缀;
- size() 获取文件大小;
- isFile() 判断是否为文件;
- fileTime() 获取文件创建时间、修改时间、最近访问时间等;
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
#include<QDebug>
#include<QFileInfo>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//弹出文件对话框,并获取到文件的属性
QString path = QFileDialog::getOpenFileName(this);
//构造出一个QFileInfo对象
QFileInfo fileInfo(path);
qDebug()<<fileInfo.fileName();
qDebug()<<fileInfo.suffix();
qDebug()<<fileInfo.path();
qDebug()<<fileInfo.size();
qDebug()<<fileInfo.isFile();
qDebug()<<fileInfo.isDir();
}

Qt 多线程
Qt 多线程概述
在 Qt 中,多线程的处理一般是通过 QThread类 来实现。QThread 代表一个在应应程序中可以独立控制的线程,也可以和进程中的其他线程共享数据。QThread 对象管理程序中的一个控制线程。
QThread 常用 API
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| run() | 线程的入口函数.. |
| start() | 通过调用 run() 开始执行线程。操作系统将根据优先级参数调度线程。如果线程已经在运行,这个函数什么也不做。 |
| currentThread() | 返回一个指向管理当前执行线程的 QThread的指针。 |
| isRunning() | 如果线程正在运行则返回true;否则返回false。 |
| sleep()/msleep()/usleep() | 使线程休眠,单位为秒 / 毫秒 / 微秒 |
| wait() | 阻塞线程,直到满足以下任何一个条件: 与此 QThread 对象关联的线程已经完成执行(即当它从run()返回时)。如果线程已经完成,这个函数将返回 true。如果线程尚未启动,它也返回 true。 已经过了几毫秒。如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回 false。 这提供了与 POSIX pthread_join() 函数类似的功能。 |
| terminate() | 终止线程的执行。线程可以立即终止,也可以不立即终止,这取决于操作系统的调度策略。在terminate() 之后使用 QThread::wait() 来确保。 |
| finished() | 当线程结束时会发出该信号,可以通过该信号来实现线程的清理⼯作。 |
使用线程
创建线程的步骤:
-
自定义一个类,继承于 QThread,并且只有一个线程处理函数(和主线程不是同一个线程),这个线程处理函数主要就是重写父类中的 run() 函数。
-
线程处理函数里面写入需要执行的复杂数据处理;
-
启动线程不能直接调用 run() 函数,需要使用对象来调用 start() 函数实现线程启动;因为存在线程安全问题,多个线程同时对界面的状态进行修改,此时就会导致界面出错,Qt选择了一刀切,针对界面的控件状态进行任何修改,务必在主线程中执行
-
线程处理函数执行结束后可以定义一个信号来告诉主线程;
-
最后关闭线程。
cpp
//////widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//连接信号槽,通过槽函数更新界面
connect(&thread,&Thread::notify,this,&Widget::handle);
//要启动一下线程
thread.start();
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//修改界面
int value = ui->lcdNumber->intValue();
value--;
ui->lcdNumber->display(value);
}
//////thread.cpp
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
//在这个run中,能否直接进去修改界面内容
//虽然不可以直接修改界面,但是可以针对事件来进行计时,当每到了一秒钟的时候,通过信号槽来通知主线程,负责更新的界面内容
for(int i = 0; i < 10;i++)
{
//sleep 本身是QThread的成员函数,就可以直接调用
sleep(1);
//发生信号通知线程
emit notify();
}
}
线程安全
实现线程互斥和同步常用的类有:
- 互斥锁:QMutex、QMutexLocker
- 条件变量:QWaitCondition
- 信号量:QSemaphore
- 读写锁:QReadLocker、QWriteLocker、QReadWriteLock
互斥锁
互斥锁是一种保护和防止多个线程同时访问同一对象实例的方法,在 Qt 中,互斥锁主要是通过
QMutex类来处理。
- QMutex
特点 :QMutex 是 Qt 框架提供的互斥锁类,用于保护共享资源的访问,实现线程间的互斥操作。
用途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全。
cpp
QMutex mutex;
mutex.lock(); //上锁
//访问共享资源
//...
mutex.unlock(); //解锁
- QMutexLocker
特点: QMutexLocker 是 QMutex 的辅助类,使用 RAII(Resource Acquisition Is Initialization)方式对互斥锁进行上锁和解锁操作。
用途:简化对互斥锁的上锁和解锁操作,避免忘记解锁导致的死锁等问题。
cpp
QMutex mutex;
{
QMutexLocker locker(&mutex); //在作⽤域内⾃动上锁
//访问共享资源
//...
} //在作⽤域结束时⾃动解锁
- QReadWriteLocker、QReadLocker、QWriteLocker
特点:
- QReadWriteLock 是读写锁类,用于控制读和写的并发访问。
- QReadLocker 用于读操作上锁,允许多个线程同时读取共享资源。
- QWriteLocker 用于写操作上锁,只允许一个线程写入共享资源。
用途:在某些情况下,多个线程可以同时读取共享数据,但只有一个线程能够进行写操作。读写锁提供了更高效的并发访问方式。
cpp
QReadWriteLock rwLock;
//在读操作中使⽤读锁
{
QReadLocker locker(&rwLock); //在作⽤域内⾃动上读锁
//读取共享资源
//...
} //在作⽤域结束时⾃动解读锁
//在写操作中使⽤写锁
{
QWriteLocker locker(&rwLock); //在作⽤域内⾃动上写锁
//修改共享资源
//...
} //在作⽤域结束时⾃动解写锁
通过锁来保持操作的原子性,创建两个子线程来去累加,各自累加五万
cpp
/////thread.h
#ifndef THREAD_H
#define THREAD_H
#include<QMutex>
#include<QThread>
#include <QWidget>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
static int num ;//添加一个static成员
void run();
static QMutex mutex;//创建一个锁来保护线程安全
};
#endif // THREAD_H
/////widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
////thread.cpp
#include "thread.h"
int Thread::num = 0;
QMutex Thread::mutex;
Thread::Thread()
{
}
void Thread::run()
{
for(int i = 0;i< 50000;i++)
{
mutex.lock();
num++;
mutex.unlock();
}
}
////widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include"thread.h"
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建两个线程对象
Thread t1;
Thread t2;
t1.start();
t2.start();
//让主线程等待两个线程执行完
t1.wait();
t2.wait();
//打印结果
qDebug()<<Thread::num;
}
Widget::~Widget()
{
delete ui;
}

cpp
#include "thread.h"
#include<QMutexLocker>
int Thread::num = 0;
QMutex Thread::mutex;
Thread::Thread()
{
}
void Thread::run()
{
for(int i = 0;i< 50000;i++)
{
//mutex.lock();
QMutexLocker locker(&mutex);
num++;
// mutex.unlock();
}
}
和动态内存一样,万一中间抛异常了,程序跳出去了,执行不到解锁那一步,依旧是内存泄漏,所以C++引入了std::lock_guard相当于是std::mutex智能指针,借助的是RAII机制,当该指针包含的大括号内的生命周期执行完毕,guard变量的生命周期也就结束,在析构的时候制动执行unlock。Qt的锁和C++的锁,本质上都是封装系统提供的锁,编写多线程程序的时候,可以使用Qt的锁,也可以使用C++的锁,C++的锁可以锁Qt的线程,底层一样,混着用也行但是不建议
条件变量
在多线程编程中,假设除了等待操作系统正在执行的线程之外,某个线程还必须等待某些条件满足才能执行,这时就会出现问题。这种情况下,线程会很自然地使用锁的机制来阻塞其他线程,因为这只是线程的轮流使用,并且该线程等待某些特定条件,人们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进入了睡眠状态,这样其他线程就可以继续运行。当条件满足时,等待条件的线程将被另一个线程唤醒。在 Qt 中,专门提供了 QWaitCondition类 来解决像上述这样的问题。
特点 :QWaitCondition 是 Qt 框架提供的条件变量类,用于线程之间的消息通信和同步。
用途:在某个条件满足时等待或唤醒线程,用于线程的同步和协调。
cpp
QMutex mutex;
QWaitCondition condition;
//在等待线程中
mutex.lock();
//检查条件是否满⾜,若不满⾜则等待
while (!conditionFullfilled())
{
condition.wait(&mutex); //等待条件满⾜并释放锁
}
//条件满⾜后继续执⾏
//...
mutex.unlock();
//在改变条件的线程中
mutex.lock();
//改变条件
changeCondition();
condition.wakeAll(); //唤醒等待的线程
mutex.unlock();
信号量
有时在多线程编程中,需要确保多个线程可以相应的访问一个数量有限的相同资源。例如,运行程序的设备可能是非常有限的内存,因此我们更希望需要大量内存的线程将这一事实考虑在内,并根据可用的内存数量进行相关操作,多线程编程中类似问题通常用信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作,而且可以跟踪可用资源的数量。
特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
用途:限制并发线程数量,用于解决一些资源有限的问题。
cpp
QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进⾏类似操作