一.概念
QWaitCondition 和 QMutex 是 Qt 框架中用于实现多线程同步的重要类。它们通常一同使用,以确保在多线程环境中对共享资源的安全访问。下面是这两个类的详细介绍及示例代码。
- QMutex(互斥量)
定义:
QMutex 是一个互斥量,用于保护对共享资源的访问,确保在同一时间只有一个线程可以访问该资源。使用 QMutex 可以避免因为多个线程同时修改同一数据而导致的数据不一致性问题。
常用功能:
- lock():加锁,阻塞其他尝试获取同一锁的线程。
- unlock():解锁,允许其他线程获取该锁。
- tryLock():尝试加锁,如果无法立即获取锁则返回 false。
- QWaitCondition(条件变量)
定义:
QWaitCondition 是一个条件变量,用于在特定条件未满足时,使线程进入等待状态。当条件满足时,可以唤醒处于等待状态的线程。
常用功能:
- wait(QMutex *mutex):等待条件满足,释放指定的互斥量以允许其他线程访问共享资源。
- wakeOne():唤醒一个等待的线程。
- wakeAll():唤醒所有等待的线程。
二.示例代码
下面是一个使用 QMutex 和 QWaitCondition 的简单示例,演示如何安全地在多个线程之间进行同步。
cpp
#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QDebug>
class WorkerThread : public QThread {
Q_OBJECT
public:
WorkerThread() : conditionMet(false) {}
void run() override {
qDebug() << "Worker thread starting...";
QMutexLocker locker(&mutex); // Lock the mutex
// Wait until the condition is met
while (!conditionMet) {
qDebug() << "Worker thread waiting...";
condition.wait(&mutex); // Wait for the condition to change
}
// If the condition is met, do some work
qDebug() << "Worker thread running...";
// Simulate work
QThread::sleep(2);
qDebug() << "Worker thread finished.";
}
void setConditionMet() {
QMutexLocker locker(&mutex); // Lock the mutex
conditionMet = true; // Change the condition
condition.wakeOne(); // Wake one waiting thread
}
private:
QMutex mutex; // Mutex for synchronization
QWaitCondition condition; // Condition variable
bool conditionMet; // Condition to be met
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
WorkerThread workerThread;
workerThread.start(); // Start the worker thread
// Simulate some delay before setting the condition
QThread::sleep(1);
workerThread.setConditionMet(); // Notify the worker thread
workerThread.wait(); // Wait for the worker thread to finish
return a.exec();
}
三.代码解释
-
WorkerThread 类: 这个类继承自 QThread,并实现了 run() 方法。在 run() 方法中,线程会等待条件变量。
-
条件控制:
- 在 run() 方法中,使用 wait() 方法进入等待状态,并释放互斥量 mutex,以允许其他线程访问共享资源。
- 当 setConditionMet() 被调用时,它会锁定互斥量,设置 conditionMet 为 true,并调用 wakeOne() 唤醒一个等待的线程。
- 主函数:
- 在主函数中,创建并启动 WorkerThread 实例。
- 通过 setConditionMet() 方法,在主线程中修改状态,唤醒工作线程。
总结
通过使用 QMutex 和 QWaitCondition,你可以有效地管理多线程中的共享资源访问和条件同步。在实际应用中,这种组合非常强大,可以避免数据竞争和确保线程间的有效沟通。
四.mutable关键字
在 C++ 中,mutable 关键字的目的是允许在 const 成员函数中修改该对象的成员变量。因此,mutable 用于修饰成员变量,在某些情况下是有意义的,尤其是当你想在 const 成员函数中修改某个变量时。
对于 mutable QMutex mutex_;
- 用途:
- 如果你将 mutex_ 声明为 mutable,那么你可以在类的 const 成员函数中对这个互斥量进行加锁和解锁。这样,如果这个类的实例是 const 类型的,但你仍然需要对某些内部状态进行保护(比如状态变量),你就可以使用这个 mutable 的 QMutex。
- 例子:
cpp
class MyClass {
public:
void doSomething() const {
QMutexLocker locker(&mutex_); // 在 const 方法中加锁
// 修改其他成员变量或访问共享资源
}
private:
mutable QMutex mutex_; // 允许在 const 方法中使用
int sharedResource_;
};
是否多余?
不多余:
- 如果给定类会有 const 成员函数,并且那些函数需要锁定 mutex_ 以访问或修改共享状态,那么将 mutex_ 声明为 mutable 是合适的。
多余:
- 如果类的成员函数不需要被声明为 const,或 mutex_ 的主要作用并不需要在 const 函数中锁定,那么 mutable 就可以认为是不必要的。
结论
总结来说,究竟将 QMutex 声明为 mutable 是否多余,取决于你的类的设计和需求。如果存在 const 成员函数并需要使用互斥量保护共享资源,则使用 mutable 是合理的。如果没有这样的需求,仅仅加锁或解锁的操作在非常规的成员函数中使用,那么 mutable 就沒有必要。