Qt中线程同步类介绍(一)
Qt中实现线程同步的类主要是:QMutex、QMutexLoker、QReadWriteLocker、QReadLocker、QWriteLocker、QSemaphore、QWaitCondition和QSemaphore。
一、基本概念
QMutex和QMutexLocker
QMutex 和QMutexLocker是基于互斥量的线程同步类。
(1)QMutex
主要提供一下3个函数:
lock():加锁。如果锁已经被其他线程占用,当前线程会阻塞(等待) 直到锁被释放;
unlock():解锁。必须和lock()成对调用,否则会导致死锁;
trylock():尝试加锁。如果锁被占用,不会阻塞,直接返回false;如果加锁成功返回true(适合不想阻塞的场景)。
QMutex简单使用示例:
c++
#include <QCoreApplication>
#include <QMutex>
#include <QThread>
#include <QDebug>
QMutex mutex; // 全局互斥锁
int sharedValue = 0; // 共享资源
void threadFunc()
{
for (int i = 0; i < 5; ++i)
{
mutex.lock(); // 加锁:进入临界区
sharedValue++; // 操作共享资源,此时只有当前线程能执行
qDebug() << QThread::currentThreadId() << "sharedValue:" << sharedValue;
mutex.unlock(); // 解锁:离开临界区,其他线程可加锁
QThread::msleep(10); // 模拟耗时操作
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Set up code that uses the Qt event loop here.
// Call a.quit() or a.exit() to quit the application.
// A not very useful example would be including
// #include <QTimer>
// near the top of the file and calling
// QTimer::singleShot(5000, &a, &QCoreApplication::quit);
// which quits the application after 5 seconds.
// If you do not need a running Qt event loop, remove the call
// to a.exec() or use the Non-Qt Plain C++ Application template.
QThread t1;
QThread t2;
QObject::connect(&t1, &QThread::started, threadFunc);
QObject::connect(&t2, &QThread::started, threadFunc);
t1.start();
t2.start();
return a.exec();
}
注意点:
必须保证lock()和unlock()成对出现!如果在临界区中抛出异常、提前 return,会导致unlock()无法执行,其他线程永远拿不到锁(死锁)。
(2)QMutexLocker
QMutexLocker是 Qt 提供的RAII 风格的锁管理类 (RAII:资源获取即初始化),可以理解为QMutex的 "自动管家"------ 它会在创建时自动加锁,销毁时自动解锁,彻底避免手动加解锁的漏写 / 错写问题。
核心逻辑:
构造函数:接收一个QMutex对象的指针 / 引用,自动调用mutex->lock();
析构函数:当QMutexLocker对象超出作用域(比如函数结束、遇到break/return),自动调用mutex->unlock()。
使用示例,替换上面的QMutex
C++
#include <QMutexLocker>
#include <QThread>
#include <QDebug>
QMutex mutex;
int sharedValue = 0;
void threadFunc()
{
for (int i = 0; i < 5; ++i)
{
QMutexLocker locker(&mutex); // 自动加锁
sharedValue++;
qDebug() << QThread::currentThreadId() << "sharedValue:" << sharedValue;
// 无需手动解锁!locker析构时自动解锁(即使这里写return也不影响)
QThread::msleep(10);
} // locker超出作用域,析构→自动解锁
}
额外实用方法
unlock():手动提前解锁(比如临界区提前结束);
relock():重新加锁(如果提前解锁后需要再次保护资源)。
(3)QMutex vs QMutexLocker 核心对比
| 特性 | QMutex | QMutexLocker |
|---|---|---|
| 加解锁方式 | 手动调用 lock ()/unlock () | 自动加锁(构造)、自动解锁(析构) |
| 安全性 | 低(易漏写 unlock ()) | 高(RAII 机制,避免死锁) |
| 代码复杂度 | 高(需手动管理成对调用) | 低(一行代码搞定加解锁) |
| 使用场景 | 简单场景 / 需手动控制解锁 | 绝大多数多线程临界区保护(推荐优先用) |
二、总结
- QMutex 是基础互斥锁,提供手动加解锁能力,核心作用是保护共享资源,但需严格保证
lock()/unlock()成对调用; - QMutexLocker 是
QMutex的封装,基于 RAII 机制自动管理锁的生命周期,能避免因异常 / 提前退出导致的死锁,是 Qt 中推荐的锁使用方式; - 实际开发中,优先使用 QMutexLocker,仅在需要手动精细控制解锁时机时(比如临界区中间需要临时解锁)才直接用 QMutex。