news 2026/3/27 1:39:02

Qt之多线程和并发_P2

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt之多线程和并发_P2

在使用多线程时需要时刻注意一点的时,多个线程在访问同一个资源时会抢夺资源,造成数据不一致,严重影响程序结果甚至崩溃。为了防止竞态条件的发生,使用多线程时需要实现线程同步,也即确保多个线程在同时使用共享资源时不会发生冲突或数据不一致。
Qt提供了互斥锁、信号量、条件变量、读写锁以及原子操作等机制实现线程同步。

文章目录

    • 互斥锁
    • 读写锁
    • 条件变量
    • 信号量

互斥锁

互斥锁QMutex是最常用的一种同步机制,用于保护一个对象或数据结构或一段代码,确保同一时间只有一个线程访问它们。

方法说明
lock()对互斥资源加锁,如果有其他线程拥有当前锁,调用此函数会阻塞直到其他线程释放了锁,通常与unlock()成套使用。注意不要对同一个互斥量多次加锁,否则会造成死锁
tryLock(int timeout)尝试获取互斥锁,如果获取成功返回true,否则返回false,如果另一个线程已经锁定了该互斥量,此函数将等待直到达到设定的超时时间(ms),如果成功获得了锁,则必须在其他线程能够成功锁定该互斥量之前,调用unlock()将其解锁。从同一线程中对同一个互斥量多次调用此函数将导致死锁。
try_lock()等同于不带参数的tryLock(),是为了与标准库兼容而提供的
unlock()释放当前持有的锁,需与lock()成对使用。试在与锁定该互斥量不同的线程中对其进行解锁会导致错误。对一个未被锁定的互斥量执行解锁操作将导致未定义行为

QMutex在使用时需要手动锁定和解锁。在简单的程序中尚可,在复杂程序中很容易忽略细节而出现问题,我们推荐QMutexLocker配合QMutex一起使用。QMutexLocker是利用RAII机制,在构造函数中加锁,析构函数中解锁,因此不需要我们手动加锁和解锁,使用更方便。为此QMutexLocker对象通常创建为局部变量,并传入一个未加锁的QMutex指针变量(若是锁定,可用relock()重新锁定或unlock())。

QMutex lock; void TestThread::run() { ... lock.lock(); dosomething(); lock.unlock(); ... } void TestThread::run() { ... QMutexLocker(&lock); dosomething(); ... }

有些场景如函数递归或重入,注意要避免重复加锁而造成死锁:

QMutex mutex; void increment() { QMutexLocker locker(&mutex); ++count; } void incrementTwice() { QMutexLocker locker(&mutex); // 第一次获取锁 increment(); // ❌ 尝试再次获取同一个锁 → 死锁! }

对于这种情况需要用到递归锁:

QRecursiveMutex mutex; void increment() { QMutexLocker locker(&mutex); ++count; } void incrementTwice() { QMutexLocker locker(&mutex); //第一次获取锁 increment(); // 第二次获取同一个锁,递归 } //析构时释放2次锁

递归锁需要记录持有者和次数,性能略低于普通锁,而且可能隐藏设计问题,因优先优化代码架构。

读写锁

QReadWriteLock读写锁允许多个线程同时以只读方式访问资源,但一旦有某个线程需要写入资源,所有其他线程(无论是读还是写)都必须被阻塞,直到写操作完成。在许多情况下,QReadWriteLockQMutex的直接替代方案。如果应用程序中存在大量并发读取操作,而写入操作相对较少,则使用QReadWriteLock是一个很好的选择。
QReadWriteLock正常情况下多个读者可以同时读(并发),一旦有写者在排队等候(即使没有开始写),系统也会阻止新读者加入,已经在读的读者可以继续读完,但新来的读者必须等待写者完成,这样设计的目的是防止读者源源不断的到来,写者永远等不到读者的时刻,写者被饿死。当有一个写者W1持有写锁,此时读者R1、R2在排队等候,如果有新的写者W2到来,即使R1、R2等得更久,W2也会优先于R1、R2获得锁,也即写者优先。

特性QMutexQReadWriteLock
锁类型互斥锁读写锁
并发读不支持允许多个线程同时读
写操作与其他任何操作互斥写时禁止所有其他读写
使用场景读写频率相近,或写多读少读多写少

QReadWriteLock类常用的方法包括lockForRead()lockForWrite()以及unlock()lockForRead()lockForWrite()需要和unlock()配套使用。

QReadWriteLock lock; void ReaderThread::run() { ... lock.lockForRead(); read_file(); lock.unlock(); ... } void WriterThread::run() { ... lock.lockForWrite(); write_file(); lock.unlock(); ... }

为了防止unlock()忘记调用,推荐使用QWriteLockerQReadLocker,这是利用了RAII机制,在构造的时候加锁,析构的自动解锁。
上述代码等效于:

void ReaderThread::run() { ... QReadLocker locker(&lock); read_file(); ... } void WriterThread::run() { ... QWriteLocker locker(&lock); write_file(); ... }

条件变量

QWaitCondition是Qt条件变量的实现,它通常配合互斥锁(QMutex)或读写锁(QReadWriteLock)使用,用来让线程在某个条件为真之前阻塞等待,由其他线程在条件改变后调用 wakeOne()/wakeAll() 唤醒。
条件变量和QMutex不同,QMutex要解决的问题是互斥,要保证同一时间只有一个线程进入,防止数据竞争,而QWaitCondition是要解决的是条件等待以及唤醒线程的问题,它能够高效的利用资源,避免无意义的轮询,充分利用CPU资源。

以生产者线程和消费者模型为例,如果只用QMutex同步线程,消费者线程需要不断地轮询检查:获取锁–>检查队列–>还为空–>睡眠–>再次获取锁…
这样如果队列为空CPU就会陷入忙等,白白浪费CPU资源,如果队列不为空,其他线程可能需要等到下一个轮询点才发现队列不为空,反应迟钝。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/18 10:41:49

Java赋能零工:众包兼职人力资源智能匹配

Java通过其强大的技术架构与丰富的功能模块,为众包兼职人力资源的智能匹配提供了高效、安全、灵活的解决方案,有效提升了零工经济的匹配效率与用工灵活性。以下从技术架构、核心功能、创新突破及行业实践四个维度进行详细阐述: 一、技术架构…

作者头像 李华
网站建设 2026/3/24 8:59:44

IPA 重签名插件上传 + 添加保姆级教程!顺便安利神仙工具 moooke

宝子们!好多人问我签名应用的时候,插件到底该在哪上传、怎么添加,今天就把压箱底的工具和教程分享给你们,还能帮你们的内容更容易被搜到哦~ 签名应用需要插件,那在哪里上传插件和添加插件呢? …

作者头像 李华
网站建设 2026/3/25 9:07:14

将Map转成对应实体

将Map转成对应实体,但存在字段类型对不上,比如map里面是字符串(存在非数字字符),但实体是double,map不需要和实体一一对应,map中有的字段,实体没有,则不会转换&#xff0…

作者头像 李华
网站建设 2026/3/21 0:19:00

Java基于Spring Boot+Vue的在线学习管理系统

所需该项目可以在最下面查看联系方式,为防止迷路可以收藏文章,以防后期找不到 这里写目录标题 项目介绍系统实现截图技术栈介绍Spring Boot与Vue结合使用的优势Spring Boot的优点Vue的优点 Spring Boot 框架结构解析Vue介绍系统执行流程Java语言介绍系统…

作者头像 李华