初始
按钮基础操作
QFont font("楷体", 20, 10, true); // 字体名称、字体大小、加粗像素、是否斜体
QPushButton* btn = new QPushButton(); // 创建按钮
btn->move(150, 100); // 设置按钮位置
btn->resize(150, 50); // 设置按钮尺寸
btn->setParent(this); // 设置按钮父节点
btn->hide(); // 隐藏按钮
btn->show(); // 显示按钮
btn->setText("文本内容"); // 设置按钮文本内容
btn->setFont(font); // 设置字体
btn->setStyleSheet("QPushButton { background-color : red; }\
QPushButton:hover { background-color : green; }\
QPushButton:pressed { background-color : rgba(170, 155, 221, 1) }");
// 让QPushButton在普通状态为红色、鼠标悬浮是绿色、按钮按下为rgb(170, 155, 221)
// 设置按钮的样式表 写法类似CSS
模态框与非模态框
- 模态框:弹出窗口后,不得操作其他窗口
- 非模态框:弹出窗口后可以继续操作其他窗口
// 模态对话框
connect(btn, &QPushButton::clicked, this, [this](){
QDialog dialog(this); // 创建一个对话框在当前场景中
dialog.resize(200, 200); // 设置对话框大小
dialog.exec(); // 将对话框加入消息队列循环中 防止被释放
// 这里不使用new方式创建 是为了能够在关闭函数之后 自动释放对象
});
// 非模态对话框
connect(btn, &QPushButton::clicked, this, [this](){
// 方式1
QDialog dialog(this); // 创建一个对话框在当前场景中
dialog.resize(200, 200); // 设置对话框大小
dialog.show(); // 通过show函数将模态框变为非模态框
dialog.exec(); // 将对话框加入消息队列循环中 防止被释放
// 这里不使用new方式创建 是为了能够在关闭函数之后 自动释放对象
// 或者使用第二方式
QDialog* dialog2 = new QDialog(this);
dialog2->show();
dialog2->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose); //设置关闭时从内存中删除对象
// 这里不用exec是因为对象已经加入到父结点中,不会被释放
});
提示弹框
#include <QFileDialog> // 文件对话框
#include <QColorDialog> // 颜色对话框
#include <QFontDialog> // 文字对话框
#include <QMessageBox> // 消息对话框:错误、警告、提示
// 文件对话框
QString fileName = QFileDialog::getOpenFileName(this, "查询文件","C:/", "*.txt *.doc");
// 获得选中文件全路径名称,指定父节点、对话框标题、默认打开路径、过滤器(显示问价类型 空格分开)
文件对话框
// 颜色对话框
QColor color = QColorDialog::getColor(Qt::blue, this, "选择颜色名");
// 获得选中颜色,指定 默认选中颜色,父节点,标题名,最后还有一个默认的menu选项自行查找
enum ColorDialogOption {
ShowAlphaChannel = 0x00000001,
NoButtons = 0x00000002,
DontUseNativeDialog = 0x00000004
};// 颜色对话框 第四个参数的三种选项,对应三种不同的选择框
颜色对话框
// 文字对话框
bool isHas = true;
QFont seleectFont = QFontDialog::getFont(&isHas, this);
// 获得选中的字体,如果用户没有选中字体而是关闭弹框则isHas为false
enum FontDialogOption {
NoButtons = 0x00000001,
DontUseNativeDialog = 0x00000002,
ScalableFonts = 0x00000004,
NonScalableFonts = 0x00000008,
MonospacedFonts = 0x00000010,
ProportionalFonts = 0x00000020
}; // getFont另一种构造函数中的弹框类型menu
文字对话框
// 消息对话框
auto btn1 = QMessageBox::critical(this, "错误对话框", "对话框内容", QMessageBox::Close|QMessageBox::No, QMessageBox::No);
// 指定父对象、标题、内容、按钮选项(通过 | 多选)、默认选中按钮
auto btn2 = QMessageBox::warning(this, "警告对话框", "警告对话框内容");
auto btn3 = QMessageBox::information(this, "提示信息对话框", "提示信息内容");
auto btn4 = QMessageBox::question(this, "问题标题", "问题内容", QMessageBox::Yes|QMessageBox::Yes, QMessageBox::Yes);
// 指定父节点、标题、内容、按钮列表、默认按钮,返回值为选中按钮的emnu
if(btn4 == QMessageBox::Yes)
{
// 你的回答是yes
}else{
// 你的回答是No
}
enum StandardButton {
// keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
NoButton = 0x00000000,
Ok = 0x00000400,
Save = 0x00000800,
SaveAll = 0x00001000,
Open = 0x00002000,
Yes = 0x00004000,
YesToAll = 0x00008000,
No = 0x00010000,
NoToAll = 0x00020000,
Abort = 0x00040000,
Retry = 0x00080000,
Ignore = 0x00100000,
Close = 0x00200000,
Cancel = 0x00400000,
Discard = 0x00800000,
Help = 0x01000000,
Apply = 0x02000000,
Reset = 0x04000000,
RestoreDefaults = 0x08000000,
FirstButton = Ok, // internal
LastButton = RestoreDefaults, // internal
YesAll = YesToAll, // obsolete
NoAll = NoToAll, // obsolete
Default = 0x00000100, // obsolete
Escape = 0x00000200, // obsolete
FlagMask = 0x00000300, // obsolete
ButtonMask = ~FlagMask // obsolete
};
消息对话框
返回值为选中按钮的menu值
常用控件
// 滑动条
QSlider* slider = new QSlider(this);
slider->setValue(20); // 设置百分比
connect(slider, &QSlider::valueChanged, [&](int value){
// 回调函数
});
// 进度条
QProgressBar* progressBar = new QProgressBar(this);
progressBar->resize(this->size().width(), 50);
progressBar->setValue(50);
progressBar->setMaximum(99); // 设置进度条最大值为99,即设置value为99时显示100%
progressBar->setOrientation(Qt::Orientation::Vertical); // 设置进度条水平/竖直
// 文本编辑框
QTextEdit* edit = new QTextEdit(this);
edit->move(100, 100);
edit->resize(200, 200); // 设置文本框大小
edit->setText("设置文本内容");
edit->setPlaceholderText("内容为空时显示的文本内容");
edit->setFontUnderline(true); // 设置下划线
QString value = edit->toPlainText(); // 获得文本字符串
connect(edit, &QTextEdit::textChanged, [](){
// 当文本内容发生变化时 触发该函数
});
窗口的几大部件
- Window Title 窗口标题
- Menu Bar 菜单栏
- Tool Bar Area 工具栏
- Dock Widget Area 浮动窗口
- Central Widget 中心内容
- Status Bar 状态栏
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置主界面的标题 仅QMainWindow有
setWindowTitle("main window title");
// 菜单栏 一般只有一个
QMenuBar *menuBar = new QMenuBar();
this->setMenuBar(menuBar); // 设置菜单栏
menuBar->addMenu("菜单1");
menuBar->addMenu("设置")->addAction("菜单2");
menuBar->addSeparator(); // 添加分隔线
QMenu* menu1 = menuBar->addMenu("菜单3");
menu1->addAction("功能1");
menuBar->addSeparator(); // 添加分隔线
QMenu* menu2 = menu1->addMenu("菜单二级");
menu2->addAction("菜单二级1");
menu2->addAction("菜单二级2");
// 工具栏 一般只有一个
QToolBar* toolBar = new QToolBar();
this->addToolBar(Qt::ToolBarArea::LeftToolBarArea,toolBar); // 第一个参数设置默认停靠区域
toolBar->addAction("编辑");
toolBar->addSeparator(); // 添加分割线
toolBar->addAction("打开");
toolBar->addSeparator(); // 添加分割线
toolBar->addAction("复制");
toolBar->addSeparator(); // 添加分割线
toolBar->addAction("粘贴");
toolBar->setAllowedAreas(Qt::ToolBarArea::LeftToolBarArea|Qt::ToolBarArea::RightToolBarArea); // 设置允许停靠的位置 默认还是在上面 但是一旦拖拽之后就只能停靠到允许的位置
toolBar->setMovable(false); // 设置不可拖拽、移动 这个设置了上面就无效了
toolBar->setFloatable(false); // 设置不可悬浮 即只能固定在设定中允许停靠的地方 而不能悬浮在任意地方
// 状态栏
QStatusBar* statusBar = new QStatusBar();
statusBar->addAction(new QAction("FPS:600"));
statusBar->addWidget(new QPushButton("FPS:300"));
statusBar->addWidget(new QLabel("PING:40"));
statusBar->addPermanentWidget(new QLabel("今天天气不错")); // 从右边添加
this->setStatusBar(statusBar);
// 中心部件/窗口
this->setCentralWidget(new QTextEdit("输入文本内容"));
// 浮动窗口 QDockWidget
QDockWidget* dockWidget = new QDockWidget("浮动窗口标题");
dockWidget->setAllowedAreas(Qt::DockWidgetArea::RightDockWidgetArea); // 设置只允许停靠在左边 默认在上面 一旦移动则不能再停靠到上面
this->addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget, Qt::Orientation::Horizontal); // 第一个参数为停靠区域 第三个参数为水平/垂直方向
}
除了代码设置界面外,Qt提供ui窗口,直接编辑界面
- 设置中文方法一:直接在右下角设置
- 设置中文方法二
ui->menutest1->setTitle("测试1");
ui->actionmenu1->setText("菜单1");
文件
普通文本
connect(act2, &QAction::triggered, [=](){
QString filePath = QFileDialog::getOpenFileName(this, "选择文件", "C:/");
textEditCentral->setText(filePath);
getFileString(filePath);
});
void MainWindow::getFileString(QString filePath)
{
QFile file(filePath);
bool couldOpen = file.open(QIODevice::ReadOnly); // 通过设置打开方式打开,返回值会是否能够打开
if(!couldOpen)
{
return;
}
// while(!file.atEnd()) // 判断文件结尾
// {
// char str[1024] = {0};
// file.readLine(str, sizeof(str)); // 通过设置读取字符串的首地址和长度从文件中读取内容
// m_centralTextEdit->append(str);
// }
QByteArray fileVal;
while(!file.atEnd())
{
fileVal += file.readLine();
}
QString temp = fileVal;
m_centralTextEdit->setText(fileVal);
file.close();
}
menu | 值 | 含义 |
---|---|---|
QIODevice::NotOpen | 0x0000 | The device is not open |
QIODevice::ReadOnly | 0x0001 | The device is open for reading. |
QIODevice::WriteOnly | 0x0002 | The device is open for writing. Note that this mode implies Truncate. |
QIODevice::ReadWrite | ReadOnly | WriteOnly |
QIODevice::Append | 0x0004 | The device is opened in append mode so that all data is written to the end of the file. |
QIODevice::Truncate | 0x0008 | If possible, the device is truncated before it is opened. All earlier contents of the device are lost. |
QIODevice::Text | 0x0010 | When reading, the end-of-line terminators are translated to ‘\n’. When writing, the end-of-line terminators are translated to the local encoding, for example ‘\r\n’ for Win32. |
QIODevice::Unbuffered | 0x0020 | Any buffer in the device is bypassed. |
connect(act2, &QAction::triggered, [=](){
QString filePath = QFileDialog::getSaveFileName(this, "另存为", "C:/文件名称.txt", "*.txt"); // 父节点 标题 默认存储路径及文件名 文件过滤器
textEditCentral->setText(filePath);
writeStringToFile(filePath);
});
void MainWindow::writeStringToFile(QString filePath)
{
QFile file(filePath);
bool couldOpen = file.open(QIODevice::WriteOnly); // 通过设置打开方式打开,返回值会是否能够打开
if(!couldOpen)
{
return;
}
QString val = m_centralTextEdit->toPlainText(); // 获得文本框内容
file.write(val.toUtf8());
file.close();
}
二进制
写入二进制
struct Student
{
int id; // 学生id
float height; // 身高
QString name; // 姓名
bool isMan; // 是否是男
friend QDataStream& operator <<(QDataStream& stream, Student& stu)
{
stream << stu.id << stu.height << stu.name << stu.isMan;
return stream;
}
friend QDataStream& operator >>(QDataStream& stream, Student& stu)
{
stream >> stu.id >> stu.height >> stu.name >> stu.isMan;
return stream;
}
};
connect(act2, &QAction::triggered, [=](){
QString filePath = QFileDialog::getSaveFileName(this, "另存为二进制文件", "C:/文件名称.bin", "*.bin"); // 父节点 标题 默认存储路径及文件名 文件过滤器
textEditCentral->setText(filePath);
writeBinToFile(filePath);
});
void MainWindow::writeBinToFile(QString filePath)
{
QFile file(filePath);
bool couldOpen = file.open(QIODevice::WriteOnly); // 通过设置打开方式打开,返回值会是否能够打开
if(!couldOpen)
{
return;
}
QString val = m_centralTextEdit->toPlainText(); // 获得文本框内容
Student stu;
stu.id = 1, stu.height = 181.1f, stu.name = "名字", stu.isMan = false;
// 数据流对象
QString testStr = "测试内容";
int testInt = 1234;
QDataStream outStream(&file); // 构造 顺便传入文件对象
outStream << val;
outStream << testInt;
outStream << testStr;
outStream << stu;
qDebug() << val << testInt << testStr;
}
读取二进制
connect(act2, &QAction::triggered, [=](){
QString filePath = QFileDialog::getOpenFileName(this, "读取二进制文件", "C:/", "*.bin");
textEditCentral->setText(filePath);
getFileString(filePath);
});
void MainWindow::getBinToFile(QString filePath)
{
QFile file(filePath);
bool couldOpen = file.open(QIODevice::ReadOnly); // 通过设置打开方式打开,返回值会是否能够打开
if(!couldOpen)
{
return;
}
QString val;
QString testStr;
int testInt;
Student stu;
// 数据流对象
QDataStream inStream(&file); // 构造 顺便传入文件对象
inStream >> val;
inStream >> testInt;
inStream >> testStr;
inStream >> stu;
qDebug() << val << testInt << testStr;
}
配置文件QSettings
存储配置文件
// 创建配置文件对象
QSetting settingsFile("config.ini");
settingsFile.saveValue("key", textEdit.toPlainText()); // 将文本框内容存入文件中
读取配置文件
QSettings settingsFile("config.ini");
textEdit->setText(settingsFile.value("key"));
Qt事件处理
鼠标事件及实例
鼠标事件包括:鼠标移动、鼠标键按下、松开、单击、双击等
protected:
void mousePressEvent(QMouseEvent *e); // 鼠标按下事件响应函数
void mouseMoveEvent(QMouseEvent *e); // 鼠标移动事件响应函数
void mouseReleaseEvent(QMouseEvent *e); // 鼠标松开时间响应函数
void mouseDoubleClickEvent(QMouseEvent *e); // 鼠标双击事件响应函数
QWidget类中存在鼠标事件方法
void MainWidget::mousePressEvent(QMouseEvent *e){
if(e->button() == Qt::LeftButton)
{
// 鼠标左键被点击
}
else if (e->button() == Qt::RightButton)
{
// 鼠标右键被点击
}
else if(e->button() == Qt::MidButton)
{
// 鼠标中间键被点击
}
// e->x() 鼠标当前x轴坐标
// e->y() 鼠标当前y轴坐标
}
键盘事件
- 重定义QWidget的
keyPressEvent()
和keyReleaseEvent()
来实现键盘事件
void keyPressEvent(QKeyEvent *e);
void keyReleaseEvent(QKeyEvent *e);
.h文件定义
void MainWidget::keyPressEvent(QKeyEvent *e)
{
if(event->modifiers() == Qt::Key_Left)
{
// 判断点的哪个键
}
}
modifiers的值 | 按下的键 |
---|---|
Qt::ControlModifier | Ctrl键 |
Qt::NoModifier | 没有修饰键按下 |
Qt::ShiftModifier | Shift键 |
Qt::AltModifier | Alt键 |
Qt::MetaModifier | Meta键 |
Qt::KeypadModifier | 小键盘键 |
Qt::GroupSwitchModifier | Mode switch 键 |
Qt::Key_Left | 左方向键 |
Qt::Key_Right | 右方向键 |
Qt::Key_Up | 上方向键 |
Qt::Key_Down | 下方向键 |
… | … |
事件过滤
Qt的事件模型中提供的事件过滤器使得一个QObject对象可以监视另一个QObject对象中的对象,通过在一个QObject对象中安装事件过滤器,可以在时间到达该对象前捕获事件,从而起到监视该对象的效果
m_label1 = new QLabel(); // 私有变量
m_label1 ->installEventFilter(this);
installEventFileter()
为QObject对象安装事件过滤器,指定this
(这里是窗体)为监听事件的对象
void QObject::InstallEventFilter(QObject* filterObj);
参数filterObj是监视事件的对象,此对象可以通过
eventFileter()
函数接收事件
如果某个事件需要被过滤,即停时正常的事件响应,则在eventFilter()
函数中返回true,否则返回false
removeEventFilter()
可以解除已安装的事件过滤器
bool MainWidget::eventFilter(QObject *watched, QEvent* event)
{
if(watched == m_label1)
{
if(event->type() == QEvent::MouseButtonPress)// 判断事件类型为鼠标点击事件
{
QMouseEvent *mouseEvent = (QMouseEvent *)(event); // 转为鼠标事件
if(mouseEvent->buttons() == Qt::LeftButton)
{
// 如果时点鼠标左键
}
}
}
}
网络相关
获取本机信息
QString localHostName = QHostInfo::localHostName();
// 获得本机主机名
QHostInfo hostInfo = QHostInfo::fromName(localHostName);
// 根据主机名获得相关主机信息 包括IP地址等
QList<QHostAddress> listAddress = hostInfo.addresses();
// 获得主机的IP地址列表
if(!listAddresss.isEmpty())
{
// do something
}
// 获得的主机IP地址列表可能为空
QHostInfo
提供一系列有关网络信息的静态函数,可以根据主机名获得分配的IP地址,也可以根据IP地址获得相应的主机名
QString detail = "";
QList<QNetWordInterface> list = QNetworkInterface::allInterfaces();
for(int i=0; i<list.count(); i++)
{
QNetworkInterface interface = list.at(i);
detail = detail + "设备:" + interface.name() + "\n";
detail = detail + "硬件地址:" + interface.hardwareAddress() + "\n";
QList<QNetworkAddressEntry> entryList = interface.addressEntries();
for(int j=0; j<entryList.count(); j++)
{
QNetworkAddressEntry entry = entryList.at(j);
detail = detail + "\t IP地址" + entry.ip().toString() + "\n";
detail = detail + "\t 子网掩码" + entry.netmask().toString() + "\n";
detail = detail + "\t 广播地址" + entry.broadcast().toString() + "\n";
}
}
QNetworkInterface
提供了一个主机IP地址和网络接口的列表
interface.name()
获得网络接口的名称
interface.hardwareAddress()
获得网络接口的硬件地址
interface.addressEnteries()
每个网络接口包括0个或多个IP地址,每个IP地址有选择性的与一个子网掩码或一个广播地址关联
QNetworkAddressEntry
存储了被网络接口支持的一个IP地址,同时还包括与之相关的子网掩码和广播地址
UDP
UDP客户端与服务器间的交互时序
QT += network
添加
class UpdServer : public QWidget
{
// other function
public solts:
void timeOut();
private:
int ports;
QUpdSocket *updSocket;
QTimer *timer;
}
void UpdServer::UpdServer()
{
ports = 5555; // 端口号
updSocket = new QUpdSocket(this);
timer = new QTimer(this); // 定时器
connet(timer, SIGNAL(timeout()), this, SLOT(timeOut()));
timer->start(1000);
}
void UpdServer::timeOut()
{
QString msg = "sendMessage";
int length = 0;
length = updSocket->writeDatagram(msg.toLatin1(), msg.length(), QHostAddress::Broadcast, port);
// 发送信息
}
上述为发送信息端,每秒发送信息
void UdpClient::UpdClient()
{
port = 5555;
udpSocket = new QUdpsSocket(this);
connet(udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived());
bool result = udpSocket->bind(port);
if(!result)
{
// 端口绑定失败
}
}
void UdpClient::dataReceived()
{
while(updSocket->hasPendingDatagrams()) // 判断UpdSocket中是否有数据可读,至少有一个数据报的时候返回true
{
QByteArray datagram;
datagram.resize(updSocket->pendingDataGramSize()); // 设置数据长度为udpSocket中的长度
updSocket->readDatagram(datagram.data(), datagram.size()); // 从updSocket中读取数据到数组中
QString msg = datagram.data();
// 获得数据
}
}
上述为接收信息端
connet(udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived());
:连接QIODevice
的readyRead()
信号,QUdpSocket
也是一个I/O设备,从QIODevice继承而来,当有数据到达I/O设备时,发出readyRead()
信号
TCP
TCP客户端与服务器间的交互时序
首显启动服务器,一段时间后启动客户端,它与此服务器经过三次握手后建立连接。此后的一段时间内,客户端向服务器发送一个请求,服务器处理这个请求,并为客户端发送一个响应。这个过程一直持续下去,直到客户端为服务器发一个文件结束符,并关闭客户端连接,接着服务器页关闭服务端的连接,结束运行或等到一个新的客户端连接
比较项 | TCP | UDP |
---|---|---|
是否连接 | 面向连接 | 无连接 |
传输可靠性 | 可靠 | 不可靠 |
流量控制 | 提供 | 不提供 |
工作方式 | 全双工 | 可以是全双工 |
应用场合 | 大量数据 | 少量数据 |
速度 | 慢 | 快 |
Qt多线程
- 多线程的优势
- 提高应用程序的响应速度
- 如果不使用多线程,当一个操作耗时很长的时候,程序无法响应键盘、鼠标、菜单操作等
- 使多CPU系统更加有效
- 改善程序结构
- 提高应用程序的响应速度
- 多线程程序有几个特点
- 多线程程序的行为无法预期,当多次执行上述程序时,每一次的运行结果都可能不同
- 多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关
- 多线册的切换可能发生在任何时刻、任何地点
- 多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的结果
多线程的简单实例
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyTHread();
protected:
void run();
}
void MyThread::run()
{
while(true)
{
qDebug() << 1;
}
}
auto workThread = new MyThread();
workThread->start();
workThread->wait();
workThread->terminate();
workThread->start()
调用QThread基类的start()
函数,此函数将启动函数run()
workThread->wait()
挂起
workThread->terminate()
停止,但线程何时停止取决于操作系统的调度策略
多线程控制
实现线程的互斥和同步常使用的类有:QMutex、QMutexLocker、QReadWriteLocker、QReadLocker、QWriteLocker\QSemaphor和QWaitCondition
class Key {
public:
Key() { key = 0 }
int createKey() { ++key; return key; }
int value() { return key; }
private:
int key;
};
这是测试用例 目标是让key的值无线加一,并且不出现重复的值
一般来说++key
可以分作三步:
- 将变量值载入寄存器
- 将寄存器中的值+1
- 将寄存器中的值写回主存
在多线程中,中间任何一步都可能被暂停转而执行其他线程,从而导致值得重复
本例中key
每次只能被一个线程使用,是临界资源
互斥量
- QMutex类
QMutex类是对互斥量的处理,它被用来保护一段临界区代码,即每次只允许一个线程访问这段代码
- QMutex类的
lock()
用于锁住互斥量- 如果互斥量处于解锁状态,则当前线程回立即抓住并且锁住它
- 如果处于锁定状态,则线程会被阻塞,直到被解锁
- QMutex类使用
unlock()
解除锁定 - QMutex提供
tryLock()
,如果互斥量已被锁定,即立即返回
class Key {
public:
Key() { key = 0 }
int createKey() {mutex.lock(); ++key; return key; mutex.unlock();}
int value() {mutex.lock(); return key; mutex.unlock()}
private:
int key;
QMutex mutex;
};
上面代码很明显发现一个问题,在return之后才能确保输出没有重复值,但是return之后会导致无法运行到解锁代码
- QMutexLocker类
Qt提供的QMutexLocker类看i简化互斥量的处理,他在构造函数中接收一个QMutex对象座位参数并将其锁定,在析构函数中解锁这个互斥量
class Key {
public:
Key() { key = 0 }
int createKey() {QMutexLocker.locker(&mutex); ++key; return key; }
int value() {QMutexLocker.locker(&mutex); return key;}
private:
int key;
QMutex mutex;
};
locker()
函数座位局部变量会在函数退出时结束其作用域,从而自动对互斥量mutex
解锁
信号量
信号量可以理解为对互斥量功能的扩展,互斥量只能锁定一次而信号量可以获取多次,它可以用来保护一定数量的同种资源
信号量的典型用力就是控制生产者/消费者之间共享的环形缓冲区
- 生产者/消费者对同步的需求
- 如果生产者过快的生产数据,将会覆盖消费者还没有读取的数据
- 如果消费者过快的读取数据,将越过生产者并且读取一些过期数据
- 上述问题的解决方案
- 首先使生产者填满整个缓冲区,然后等待消费者读取整个缓冲区,这个是笨拙的方法
- 使生产者和消费者线程同时分别操作缓冲区的不同部分,这是一种高效的方法