Qt

初始

按钮基础操作

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, [](){
    // 当文本内容发生变化时 触发该函数
});

窗口的几大部件

窗口5部件

  1. Window Title 窗口标题
  2. Menu Bar 菜单栏
  3. Tool Bar Area 工具栏
  4. Dock Widget Area 浮动窗口
  5. Central Widget 中心内容
  6. 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窗口,直接编辑界面

设置菜单栏

  1. 设置中文方法一:直接在右下角设置
    直接UI界面修改

在这里插入图片描述

  1. 设置中文方法二
    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::NotOpen0x0000The device is not open
QIODevice::ReadOnly0x0001The device is open for reading.
QIODevice::WriteOnly0x0002The device is open for writing. Note that this mode implies Truncate.
QIODevice::ReadWriteReadOnlyWriteOnly
QIODevice::Append0x0004The device is opened in append mode so that all data is written to the end of the file.
QIODevice::Truncate0x0008If possible, the device is truncated before it is opened. All earlier contents of the device are lost.
QIODevice::Text0x0010When 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::Unbuffered0x0020Any 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::ControlModifierCtrl键
Qt::NoModifier没有修饰键按下
Qt::ShiftModifierShift键
Qt::AltModifierAlt键
Qt::MetaModifierMeta键
Qt::KeypadModifier小键盘键
Qt::GroupSwitchModifierMode 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());:连接QIODevicereadyRead()信号,QUdpSocket也是一个I/O设备,从QIODevice继承而来,当有数据到达I/O设备时,发出readyRead()信号

TCP

在这里插入图片描述

TCP客户端与服务器间的交互时序
首显启动服务器,一段时间后启动客户端,它与此服务器经过三次握手后建立连接。此后的一段时间内,客户端向服务器发送一个请求,服务器处理这个请求,并为客户端发送一个响应。这个过程一直持续下去,直到客户端为服务器发一个文件结束符,并关闭客户端连接,接着服务器页关闭服务端的连接,结束运行或等到一个新的客户端连接

比较项TCPUDP
是否连接面向连接无连接
传输可靠性可靠不可靠
流量控制提供不提供
工作方式全双工可以是全双工
应用场合大量数据少量数据
速度

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. 将变量值载入寄存器
  2. 将寄存器中的值+1
  3. 将寄存器中的值写回主存

在多线程中,中间任何一步都可能被暂停转而执行其他线程,从而导致值得重复
本例中key每次只能被一个线程使用,是临界资源

互斥量

  1. 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之后会导致无法运行到解锁代码

  1. 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解锁

信号量

信号量可以理解为对互斥量功能的扩展,互斥量只能锁定一次而信号量可以获取多次,它可以用来保护一定数量的同种资源
信号量的典型用力就是控制生产者/消费者之间共享的环形缓冲区

  • 生产者/消费者对同步的需求
    • 如果生产者过快的生产数据,将会覆盖消费者还没有读取的数据
    • 如果消费者过快的读取数据,将越过生产者并且读取一些过期数据
  • 上述问题的解决方案
    • 首先使生产者填满整个缓冲区,然后等待消费者读取整个缓冲区,这个是笨拙的方法
    • 使生产者和消费者线程同时分别操作缓冲区的不同部分,这是一种高效的方法
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值