Qt/C++面试【速通笔记六】—Qt 中的线程同步

在多线程编程中,多个线程同时访问共享资源时,可能会出现数据不一致或者错误的情况。这时,我们需要线程同步机制来保证程序的正确性。Qt 提供了多种线程同步方式,每种方式适用于不同的场景。


1. 互斥锁(QMutex)

QMutex 是最常用的线程同步机制之一,它用于保护共享资源,确保同一时刻只有一个线程可以访问资源。互斥锁(Mutex)也叫排他锁,是一种独占的锁。

代码示例:
#include <QMutex>
#include <QThread>
#include <QDebug>QMutex mutex;  // 声明一个互斥锁
int sharedData = 0;  // 共享数据// 安全地递增共享数据
void safeIncrement() {mutex.lock();  // 加锁,确保只有一个线程能访问共享数据sharedData++;  // 对共享数据进行操作mutex.unlock();  // 解锁,允许其他线程访问
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 1000; ++i) {safeIncrement();  // 调用安全递增函数}}
};int main() {MyThread thread1, thread2;  // 创建两个线程thread1.start();  // 启动线程1thread2.start();  // 启动线程2thread1.wait();  // 等待线程1结束thread2.wait();  // 等待线程2结束qDebug() << "最终共享数据的值:" << sharedData;  // 输出共享数据的最终值
}

注释:

  • 在多线程环境下,sharedData 是多个线程共享的资源。我们通过 QMutex 进行加锁和解锁操作,确保同一时刻只有一个线程可以访问和修改 sharedData

2. 读写锁(QReadWriteLock)

QReadWriteLock 是一种更细粒度的锁机制。它允许多个线程同时读取数据,但在写数据时,必须独占锁。这样,在读取时多个线程可以并发执行,但写入时会阻塞其他线程的读取和写入。

代码示例:
#include <QReadWriteLock>
#include <QThread>
#include <QDebug>QReadWriteLock lock;  // 声明读写锁
int sharedData = 0;  // 共享数据// 读取共享数据
void readData() {lock.lockForRead();  // 加读锁,允许多个线程并发读取qDebug() << "读取共享数据:" << sharedData;lock.unlock();  // 解锁
}// 写入共享数据
void writeData(int value) {lock.lockForWrite();  // 加写锁,独占锁sharedData = value;qDebug() << "写入共享数据:" << sharedData;lock.unlock();  // 解锁
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 5; ++i) {readData();  // 读取共享数据writeData(i);  // 写入共享数据}}
};int main() {MyThread thread1, thread2;  // 创建两个线程thread1.start();  // 启动线程1thread2.start();  // 启动线程2thread1.wait();  // 等待线程1结束thread2.wait();  // 等待线程2结束
}

注释:

  • QReadWriteLock 的好处是允许多个线程同时读取数据,但写操作时会阻塞其他线程的读取和写入。适合数据不经常改变但需要频繁读取的场景。

3. 事件和信号槽机制(QEvent, QSignalEmitter)

Qt 的信号和槽机制是非常强大的一种线程间通信方式。它通过事件机制,让一个线程发出信号,另一个线程接收并响应这个信号,避免了显式的锁操作。信号槽机制通过 Qt 自己的事件循环来实现线程安全的通信。

代码示例:
worker.h — Worker 类定义
#ifndef WORKER_H
#define WORKER_H#include <QObject>
#include <QThread>
#include <QDebug>class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}void doWork() {qDebug() << "工作线程中执行:" << QThread::currentThread();}
};#endif // WORKER_H

#### **`workerthread.h` — WorkerThread 类定义**```cpp
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H#include <QThread>
#include <QObject>
#include "worker.h"class WorkerThread : public QThread {Q_OBJECT
public:explicit WorkerThread(Worker *worker, QObject *parent = nullptr) : QThread(parent), m_worker(worker) {}protected:void run() override {emit workSignal();}signals:void workSignal();private:Worker *m_worker;
};#endif // WORKERTHREAD_H
main.cpp — 主函数
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include "worker.h"
#include "workerthread.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Worker worker;  // 创建工作对象WorkerThread thread(&worker);  // 创建工作线程// 连接信号和槽QObject::connect(&thread, &WorkerThread::workSignal, &worker, &Worker::doWork);thread.start();  // 启动线程thread.wait();   // 等待线程结束return a.exec();
}

4. 条件变量(QWaitCondition)

QWaitCondition 允许线程在等待某个条件满足时释放锁,并在条件满足时唤醒其他线程。通常与 QMutex 一起使用,用于在多线程间传递信号和等待条件。

代码示例:
#include <QWaitCondition>
#include <QMutex>
#include <QThread>
#include <QDebug>QMutex mutex;  // 声明一个互斥锁
QWaitCondition condition;  // 声明一个条件变量
bool ready = false;  // 共享条件变量// 等待条件
void waitForCondition() {mutex.lock();while (!ready) {  // 如果条件不满足,线程会阻塞condition.wait(&mutex);  // 等待}qDebug() << "条件满足,线程继续执行!";mutex.unlock();
}// 通知条件满足
void notifyCondition() {mutex.lock();ready = true;  // 改变条件状态condition.wakeOne();  // 唤醒一个等待的线程mutex.unlock();
}int main() {QThread* thread1 = new QThread();QThread* thread2 = new QThread();QObject::connect(thread1, &QThread::started, waitForCondition);  // 连接信号和槽QObject::connect(thread2, &QThread::started, notifyCondition);  // 连接信号和槽thread1->start();  // 启动线程1thread2->start();  // 启动线程2thread1->wait();  // 等待线程1结束thread2->wait();  // 等待线程2结束
}

注释:

  • QWaitCondition 适用于需要线程等待某个条件满足后再继续执行的情况。thread1 会等待 ready 变量为真,然后继续执行,而 thread2 会改变条件并唤醒 thread1

5. 原子操作(QAtomicInt 和 QAtomicPointer)

QAtomicIntQAtomicPointer 提供了线程安全的原子操作,不需要加锁即可安全地操作共享变量。适用于计数器等简单的数值操作。

代码示例:
#include <QAtomicInt>
#include <QThread>
#include <QDebug>QAtomicInt counter(0);  // 声明原子计数器// 原子递增计数器
void incrementCounter() {counter.fetchAndAddOrdered(1);  // 原子递增操作
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 1000; ++i) {incrementCounter();  // 调用原子递增}}
};int main() {MyThread thread1, thread2;  // 创建两个线程thread1.start();  // 启动线程1thread2.start();  // 启动线程2thread1.wait();  // 等待线程1结束thread2.wait();  // 等待线程2结束qDebug() << "最终计数器的值:" << counter;  // 输出计数器的最终值
}

注释:

  • QAtomicInt 允许我们进行线程安全的计数操作,无需加锁,适用于高效的原子操作。

线程同步方法对比

同步方法优点缺点最适用场景
QMutex简单、直观,适合保护共享资源每次访问资源都需要加锁,可能会影响性能需要保证共享资源被多个线程安全访问的场景
QReadWriteLock允许多个线程并发读取,适用于读多写少的情况写入时需要独占锁,可能会导致读取阻塞数据经常被读取,但更新不频繁的场景
信号与槽实现跨线程通信,不需要显式的锁适用于信号发出的线程和接收的线程需要有明确的关联需要线程间通信,且希望简化锁操作的场景
QWaitCondition允许线程等待条件满足时阻塞和唤醒其他线程比较适合于等待某个条件的场景,使用复杂线程需要等待特定条件发生的场景
原子操作高效、简单,避免了使用锁的复杂性只适用于简单的数值或指针操作高性能计数器、指针操作等简单数值操作的场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/79002.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/79002.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK-17 保姆级安装教程(附安装包)

文章目录 一、下载二、安装三、验证是否安装成功1、看 java 和 javac 是否可用2、看 java 和 javac 的版本号是否无问题 一、下载 JDK-17_windows-x64_bin.exe 二、安装 三、验证是否安装成功 java&#xff1a;执行工具 javac&#xff1a;编译工具 1、看 java 和 javac 是否…

【LeetCode Hot100】回溯篇

前言 本文用于整理LeetCode Hot100中题目解答&#xff0c;因题目比较简单且更多是为了面试快速写出正确思路&#xff0c;只做简单题意解读和一句话题解方便记忆。但代码会全部给出&#xff0c;方便大家整理代码思路。 46. 全排列 一句话题意 给定一个无重复数字的序列&#xf…

pytest-前后置及fixture运用

1.pytest中的xunit风格前后置处理 pytest中用例的前后置可以直接使用类似于unittest中的前后置处理&#xff0c;但是pytest中的前后置处理方式更 加丰富&#xff0c;分为模块级、类级、方法级、函数级等不同等级的前后置处理&#xff0c;具体见下面的代码&#xff1a; test_…

使用scipy求解优化问题

一、求解二次规划问题 min(X.T * P * X C.T * X) s.t. Xi > 0 ∑Xi 1 1.定义目标函数 def objective(x):return 0.5 * np.dot(x, np.dot(P, x)) np.dot(c, x)2. 定义等式约束 def equality_constraint(x):return np.sum(x) - 1 3.定义边界约束&#xff1a;x # …

C++初阶-STL简介

目录 1.什么是STL 2.STL的版本 3.STL的六大组件 4.STL的重要性 4.1在笔试中 4.2在面试中 4.3.在公司中 5.如何学习STL 6.总结和之后的规划 1.什么是STL STL&#xff08;standard template library-标准模板库&#xff09;&#xff1b;是C标准库的重要组成部分&#xf…

kivy android打包buildozer.spec GUI配置

这个适合刚刚学习kivyd的道友使用&#xff0c;后面看情况更新 代码 import tkinter as tk from tkinter import ttk, filedialog, messagebox, simpledialog import configparser import os import json # 新增导入class BuildozerConfigTool:def __init__(self, master):se…

MOOS-ivp使用(一)——水下机器人系统的入门与使用

MOOS-ivp使用&#xff08;一&#xff09;——水下机器人系统的入门与使用 MOOS-ivp&#xff08;Marine Operational Oceanographic System for Intelligent Vehicle Planning&#xff09;是专为水下机器人&#xff08;如AUV&#xff09;设计的开源框架。类似于ROS&#xff0c;…

电子病历高质量语料库构建方法与架构项目(智能质控体系建设篇)

引言 随着人工智能技术的迅猛发展,医疗信息化建设正经历着前所未有的变革。电子病历作为医疗机构的核心数据资产,其质量直接关系到临床决策的准确性和医疗安全。传统的病历质控工作主要依赖人工审核,存在效率低下、主观性强、覆盖面有限等问题。近年来,基于人工智能技术的…

react学习笔记4——React UI组件库与redux

流行的开源React UI组件库 material-ui(国外) 官网: http://www.material-ui.com/#/github: GitHub - mui/material-ui: Material UI: Comprehensive React component library that implements Googles Material Design. Free forever. ant-design(国内蚂蚁金服) 官网: Ant…

GPU集群搭建

1. 硬件规划与采购 GPU 服务器&#xff1a;挑选契合需求的 GPU 服务器&#xff0c;像 NVIDIA DGX 系列就不错&#xff0c;它集成了多个高性能 GPU。网络设备&#xff1a;高速网络设备不可或缺&#xff0c;例如万兆以太网交换机或者 InfiniBand 交换机&#xff0c;以此保证节点…

ZYNQ 纯PL端逻辑资源程序固化流程

ZYNQ 纯PL端逻辑资源程序固化 ZYNQ的程序固化流程比传统的FPGA固化流程复杂很多&#xff0c;Vivado生成的bit文件无法直接固化在ZYNQ芯片中。因为ZYNQ 非易失性存储器的引脚&#xff08;如 SD 卡、QSPI Flash&#xff09;是 ZYNQ PS 部分的专用引脚。这些非易失性存储器由 PS …

[计算机科学#6]:从锁存器到内存,计算机存储的构建与原理

【核知坊】&#xff1a;释放青春想象&#xff0c;码动全新视野。 我们希望使用精简的信息传达知识的骨架&#xff0c;启发创造者开启创造之路&#xff01;&#xff01;&#xff01; 内容摘要&#xff1a;在上一篇文章中&#xff0c;我们深入了解了计算机如…

如何删除Google Chrome中的所有历史记录【一键清除】

谷歌浏览器记录了用户访问过的网站。这方便了查找&#xff0c;但有时也需要清理。删除所有历史记录很简单&#xff0c;只要按照以下步骤操作。 1. 打开谷歌浏览器 首先要启动谷歌浏览器。点击右上角的三个点&#xff0c;进入主菜单。 2. 进入历史记录界面 在菜单中找到“历史…

关于浏览器对于HTML实体编码,urlencode,Unicode解析

目录 HTML实体编码 URL编码 Unicode编码 解析层次逻辑 为什么<script></script>不可以编码符号 为什么不能编码JavaScript:协议 为什么RCDATA标签中的都会被解析成文本 为什么HTML编码了<>无法执行 HTML实体编码 通过特殊语法&#xff08;<、>…

【数据分享】2020年中国高精度森林覆盖数据集(免费获取)

森林作为全球陆地生态系统的主体&#xff0c;分布面积广、结构复杂&#xff0c;承担着调节气候、维护生态安全、改善环境等方面的重要作用。我国的森林资源丰富&#xff0c;据《中国森林资源报告&#xff1a;2014—2018》统计&#xff0c;我国森林覆盖率已经达到23.04%。森林覆…

C语言学习之动态内存的管理

学完前面的C语言内容后&#xff0c;我们之前给内存开辟空间的方式是这样的。 int val20; char arr[10]{0}; 我们发现这个方式有两个弊端&#xff1a;空间是固定的&#xff1b;同时在声明的时候必须指定数组的长度&#xff0c;一旦确定了大小就不能调整的。 而实际应用的过程中…

【深度学习-Day 2】图解线性代数:从标量到张量,理解深度学习的数据表示与运算

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…

首页数据展示

排版 现在做首页的排版&#xff0c;依旧是偷antd里面的东西 使用card包裹list的样式 import React from react import axios import { Card, Col, Row, List } from antd import { EditOutlined, EllipsisOutlined, SettingOutlined } from ant-design/icons; import { Avat…

使用Set和Map解题思路

前言 Set和Map这两种数据结构,在解决一些题上&#xff0c;效率很高。跟大家简单分享一些题以及如何使用Set和Map去解决这些题目。 题目链接 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 138. 随机链表的复制 - 力扣&#xff08;LeetCode&#xff09; 旧…