当前位置: 首页 > news >正文

Qt 线程同步机制:互斥锁、信号量等

在 Qt 多线程编程中,线程同步是确保多个线程安全访问共享资源的关键技术。Qt 提供了多种线程同步机制,包括互斥锁、读写锁、信号量、条件变量等。本文将深入探讨这些同步机制的使用场景、实现原理及最佳实践。

一、互斥锁(QMutex)

1. 基本互斥锁
class Counter {
public:void increment() {QMutexLocker locker(&m_mutex);m_count++;}void decrement() {QMutexLocker locker(&m_mutex);m_count--;}int value() const {QMutexLocker locker(&m_mutex);return m_count;}private:mutable QMutex m_mutex;int m_count = 0;
};
2. 递归互斥锁
class RecursiveExample {
public:RecursiveExample() : m_mutex(QMutex::Recursive) {}void outerFunction() {QMutexLocker locker(&m_mutex);// 执行一些操作// 可以在持有锁的情况下调用 innerFunctioninnerFunction();}void innerFunction() {QMutexLocker locker(&m_mutex);// 执行一些操作}private:QMutex m_mutex;
};
3. 尝试锁定
void tryLockExample() {QMutex mutex;// 尝试锁定,立即返回if (mutex.tryLock()) {try {// 访问共享资源} finally {mutex.unlock();}} else {// 无法获取锁,执行其他操作}// 尝试锁定,超时返回if (mutex.tryLock(100)) { // 等待 100 毫秒try {// 访问共享资源} finally {mutex.unlock();}} else {// 超时,执行其他操作}
}

二、读写锁(QReadWriteLock)

1. 读写锁的基本使用
class DataCache {
public:QByteArray data() const {QReadLocker locker(&m_lock);return m_data;}void setData(const QByteArray &data) {QWriteLocker locker(&m_lock);m_data = data;}private:mutable QReadWriteLock m_lock;QByteArray m_data;
};
2. 高级读写锁应用
class ConcurrentMap {
public:QString value(const QString &key) const {QReadLocker locker(&m_lock);return m_map.value(key);}void insert(const QString &key, const QString &value) {QWriteLocker locker(&m_lock);m_map.insert(key, value);}void remove(const QString &key) {QWriteLocker locker(&m_lock);m_map.remove(key);}private:mutable QReadWriteLock m_lock;QMap<QString, QString> m_map;
};

三、信号量(QSemaphore)

1. 信号量基本原理
// 生产者-消费者示例
class ProducerConsumer {
public:ProducerConsumer(int bufferSize) : m_freeSlots(bufferSize), m_usedSlots(0) {}void produce(const QString &item) {// 等待空闲槽位m_freeSlots.acquire();// 临界区{QMutexLocker locker(&m_mutex);m_buffer.enqueue(item);}// 通知有可用项m_usedSlots.release();}QString consume() {// 等待可用项m_usedSlots.acquire();QString item;// 临界区{QMutexLocker locker(&m_mutex);item = m_buffer.dequeue();}// 通知有空槽位m_freeSlots.release();return item;}private:QQueue<QString> m_buffer;QMutex m_mutex;QSemaphore m_freeSlots;QSemaphore m_usedSlots;
};
2. 资源管理示例
class ResourcePool {
public:explicit ResourcePool(int maxResources) : m_semaphore(maxResources) {}QSharedPointer<Resource> acquireResource() {// 等待资源可用m_semaphore.acquire();// 从池中获取资源或创建新资源QMutexLocker locker(&m_mutex);if (!m_resources.isEmpty()) {QSharedPointer<Resource> resource = m_resources.takeFirst();return resource;} else {return QSharedPointer<Resource>(new Resource());}}void releaseResource(const QSharedPointer<Resource> &resource) {// 将资源放回池中QMutexLocker locker(&m_mutex);m_resources.append(resource);// 释放信号量m_semaphore.release();}private:QList<QSharedPointer<Resource>> m_resources;QMutex m_mutex;QSemaphore m_semaphore;
};

四、条件变量(QWaitCondition)

1. 条件变量基本用法
class Queue {
public:void enqueue(const QString &item) {QMutexLocker locker(&m_mutex);// 等待队列不满while (m_queue.size() >= m_maxSize) {m_notFull.wait(&m_mutex);}m_queue.enqueue(item);// 通知队列非空m_notEmpty.wakeOne();}QString dequeue() {QMutexLocker locker(&m_mutex);// 等待队列非空while (m_queue.isEmpty()) {m_notEmpty.wait(&m_mutex);}QString item = m_queue.dequeue();// 通知队列不满m_notFull.wakeOne();return item;}private:QQueue<QString> m_queue;QMutex m_mutex;QWaitCondition m_notEmpty;QWaitCondition m_notFull;int m_maxSize = 10;
};
2. 多线程任务协调
class TaskCoordinator {
public:void workerReady() {QMutexLocker locker(&m_mutex);m_readyWorkers++;// 所有工作线程都准备好了if (m_readyWorkers == m_totalWorkers) {m_allReady.wakeAll();} else {// 等待其他工作线程m_notAllReady.wait(&m_mutex);}}void startWorkers() {QMutexLocker locker(&m_mutex);// 等待所有工作线程准备好while (m_readyWorkers < m_totalWorkers) {m_allReady.wait(&m_mutex);}// 通知所有工作线程开始工作m_notAllReady.wakeAll();}private:QMutex m_mutex;QWaitCondition m_allReady;QWaitCondition m_notAllReady;int m_readyWorkers = 0;int m_totalWorkers = 0;
};

五、自旋锁(QSpinLock)

1. 自旋锁基本用法
class AtomicCounter {
public:void increment() {QSpinLocker locker(&m_spinLock);m_value++;}void decrement() {QSpinLocker locker(&m_spinLock);m_value--;}int value() const {QSpinLocker locker(&m_spinLock);return m_value;}private:mutable QSpinLock m_spinLock;int m_value = 0;
};
2. 自旋锁与互斥锁性能对比
// 测试函数
void testSpinLockVsMutex() {const int iterations = 1000000;// 测试 QMutex{QMutex mutex;int value = 0;QTime timer;timer.start();for (int i = 0; i < iterations; ++i) {QMutexLocker locker(&mutex);value++;}qDebug() << "QMutex time:" << timer.elapsed() << "ms";}// 测试 QSpinLock{QSpinLock spinLock;int value = 0;QTime timer;timer.start();for (int i = 0; i < iterations; ++i) {QSpinLocker locker(&spinLock);value++;}qDebug() << "QSpinLock time:" << timer.elapsed() << "ms";}
}

六、线程安全队列实现

1. 基于互斥锁的线程安全队列
template <typename T>
class ThreadSafeQueue {
public:void enqueue(const T &value) {QMutexLocker locker(&m_mutex);m_queue.enqueue(value);m_condition.wakeOne();}T dequeue() {QMutexLocker locker(&m_mutex);// 等待队列中有元素while (m_queue.isEmpty()) {m_condition.wait(&m_mutex);}return m_queue.dequeue();}bool tryDequeue(T &value, int timeout = 0) {QMutexLocker locker(&m_mutex);// 等待队列中有元素,直到超时if (timeout > 0 && m_queue.isEmpty()) {if (!m_condition.wait(&m_mutex, timeout)) {return false;}}if (m_queue.isEmpty()) {return false;}value = m_queue.dequeue();return true;}bool isEmpty() const {QMutexLocker locker(&m_mutex);return m_queue.isEmpty();}int size() const {QMutexLocker locker(&m_mutex);return m_queue.size();}private:QQueue<T> m_queue;mutable QMutex m_mutex;QWaitCondition m_condition;
};
2. 无锁队列实现(原子操作)
template <typename T>
class LockFreeQueue {
public:void enqueue(const T &value) {Node *newNode = new Node(value);Node *oldTail = m_tail.load();while (!m_tail.compare_exchange_weak(oldTail, newNode)) {// CAS 失败,重试}oldTail->next = newNode;}bool dequeue(T &value) {Node *oldHead = m_head.load();if (oldHead == m_tail.load()) {return false;  // 队列为空}Node *newHead = oldHead->next;if (m_head.compare_exchange_weak(oldHead, newHead)) {value = oldHead->data;delete oldHead;return true;}return false;  // CAS 失败,重试}private:struct Node {T data;Node *next;explicit Node(const T &value) : data(value), next(nullptr) {}};std::atomic<Node*> m_head;std::atomic<Node*> m_tail;
};

七、同步机制选择指南

同步机制适用场景
QMutex保护短时间内的关键代码段,防止多个线程同时访问共享资源
QReadWriteLock读多写少的场景,允许多个线程同时读取共享资源,但写操作需要独占访问
QSemaphore控制对有限资源的访问,如线程池、连接池等
QWaitCondition需要线程间复杂协调的场景,如生产者-消费者模型、任务同步等
QSpinLock锁持有时间极短,且线程不希望在等待锁时被挂起的场景
QRecursiveMutex同一线程需要多次获取锁的场景,如递归函数中使用锁

八、最佳实践

1. 避免死锁
// 错误示例:可能导致死锁
void deadlockExample() {static QMutex mutex1, mutex2;// 线程 1[&]() {QMutexLocker locker1(&mutex1);QThread::msleep(100);  // 增加死锁概率QMutexLocker locker2(&mutex2);// 访问共享资源}();// 线程 2[&]() {QMutexLocker locker2(&mutex2);QThread::msleep(100);  // 增加死锁概率QMutexLocker locker1(&mutex1);// 访问共享资源}();
}// 正确示例:按固定顺序获取锁
void deadlockFreeExample() {static QMutex mutex1, mutex2;// 线程 1[&]() {QMutexLocker locker1(&mutex1);QMutexLocker locker2(&mutex2);// 访问共享资源}();// 线程 2[&]() {// 按相同顺序获取锁QMutexLocker locker1(&mutex1);QMutexLocker locker2(&mutex2);// 访问共享资源}();
}
2. 最小化锁的粒度
// 不良实践:锁粒度太大
void badPractice() {QMutexLocker locker(&m_mutex);// 执行不需要锁保护的操作doSomeWork();// 只需要保护关键部分{// 关键代码段sharedResource.access();}// 执行不需要锁保护的操作doMoreWork();
}// 良好实践:锁粒度最小化
void goodPractice() {// 执行不需要锁保护的操作doSomeWork();// 只锁定关键部分{QMutexLocker locker(&m_mutex);sharedResource.access();}// 执行不需要锁保护的操作doMoreWork();
}

九、总结

Qt 提供了多种线程同步机制,每种机制都有其特定的适用场景。在实际开发中,应根据具体需求选择合适的同步机制,并遵循以下原则:

  1. 最小化共享资源:尽量减少线程间共享的数据,降低同步需求
  2. 选择合适的同步原语:根据访问模式选择互斥锁、读写锁或信号量等
  3. 保持锁的粒度最小:只锁定真正需要保护的代码段
  4. 避免死锁:按固定顺序获取锁,避免嵌套锁
  5. 优先使用 RAII 风格的锁管理:如 QMutexLocker、QReadLocker 等
  6. 考虑无锁数据结构:对于高性能要求的场景,考虑使用原子操作实现无锁数据结构

通过合理使用这些同步机制和最佳实践,可以构建高效、稳定的多线程应用,避免常见的线程安全问题。

http://www.dtcms.com/a/299157.html

相关文章:

  • VTK交互——ImageRegion
  • Mixture-of-Recursions: 混合递归模型,通过学习动态递归深度,以实现对自适应Token级计算的有效适配
  • RK3568笔记九十二:QT使用Opencv显示摄像头
  • 基于RK3588+国产实时系统的隧道掘进机智能操控终端应用
  • NOIP普及组|2009T1多项式输出
  • 20250726让荣品的PRO-RK3566开发板通过TF卡刷Buildroot系统
  • 详解力扣高频SQL50题之1141. 查询近30天活跃用户数【简单】
  • 工具 | 解决 VSCode 中的 Delete CR 问题
  • 黑屏运维OceanBase数据库的常见案例
  • Java中配置两个r2db连接不同的数据库
  • LeetCode 854:相似度为 K 的字符串
  • RabbitMQ面试精讲 Day 5:Virtual Host与权限控制
  • 力扣 hot100 Day56
  • 香港本地和国际金融科技应用
  • 比特币如何实现去中心化?技术架构与机制解析
  • 2025年7月26日训练日志
  • MySQL 基本查询
  • SGLang 核心技术详解
  • IPSec IKE端口理解
  • Bert项目--新闻标题文本分类
  • 备份一下我的 mac mini 的环境变量配置情况
  • Java基础-文件操作
  • 尝试几道算法题,提升python编程思维
  • JavaSE:初识数组
  • 7月26日星期六今日早报简报微语报早读
  • kafka的消息存储机制和查询机制
  • Android Data Binding 深度解析与实践指南
  • Ubuntu22.04提示找不到python命令的解决方案
  • 测试人员先写测试要点,还是 测试场景?
  • 可调谐激光器原理与设计 【DFB 与 DBR 激光器剖析】