QMutex
一、描述
QMutex是互斥量,用于给对象、数据结构、代码段加上索使它们成为互斥体,从而一次只有一个线程可以访问这些内容。
例如,假设有一个方法在两行上向用户打印一条消息:
int number = 6;
void method1()
{
number *= 5;
number /= 4;
}
void method2()
{
number *= 3;
number /= 2;
}
如果连续调用这两个方法,会发生以下情况:
// method1()
number *= 5; // number = 30
number /= 4; // number = 7
// method2()
number *= 3; // number = 21
number /= 2; // number = 10
如果从两个线程同时调用这两个方法,则可能会产生以下序列:
// 线程1调用method1()
number *= 5; // number = 30
// 线程2调用method2(),线程1被操作系统置于睡眠状态
number *= 3; // number = 90
number /= 2; // number = 45
//线程1接着执行
number /= 4; // number = 11,而不是10
如果添加一个互斥量,可以得到我们想要的结果:
QMutex mutex;
int number = 6;
void method1()
{
mutex.lock();
number *= 5;
number /= 4;
mutex.unlock();
}
void method2()
{
mutex.lock();
number *= 3;
number /= 2;
mutex.unlock();
}
在任何给定时间只有一个线程可以修改数字并且结果是正确的。
当在线程中调用 lock() 时,其他尝试在同一位置调用 lock() 的线程将阻塞,直到获得锁的线程调用 unlock()。
QMutex 在非竞争情况下做了速度优化。如果互斥量没有争用,则非递归 QMutex 将不会分配内存。它的构建和销毁几乎没有任何开销,这意味着可以将许多互斥量作为其他类的一部分。
二、类型成员
1、enum QMutex::RecursionMode:递归模式
- Recursive:递归模式。一个线程可以多次锁定同一个互斥量,并且在进行相应数量的 unlock() 调用之前互斥锁不会被解锁。(比NonRecursive更慢更耗内存)
- NonRecursive:非递归模式,默认值。一个线程只能锁定一个互斥量一次。
三、成员函数
1、bool isRecursive()
互斥量是否递归的。
2、void lock()
锁定互斥量。如果已有另一个线程锁定了互斥量,则此调用将阻塞,直到该线程将其解锁。
3、void unlock()
解锁互斥量。尝试在与锁定它的线程不同的线程中解锁互斥锁会导致错误。解锁未锁定的互斥量会导致未定义的行为。
4、bool tryLock(int timeout = 0)
尝试锁定互斥量。成功则返回true(获得了锁)。如果已有另一个线程锁定了互斥量,此函数将最多等待timeout毫秒,之后如果互斥量还是被锁定状态则返回false。
传递一个负数相当于调用 lock(),即该函数将永远等待直到可以锁定互斥锁。
5、bool try_lock()
和tryLock()功能一样,提供此函数是为了与标准库std::tryLock()兼容。
6、~QMutex()
销毁互斥量。销毁正在使用的互斥量会导致未定义行为。
QRecursiveMutex
QRecursiveMutex 类继承自 QMutex。它与 QMutex 的不同之处在于接受来自同一线程任意次数的 lock() 调用。QMutex 在这种情况下会死锁。
QRecursiveMutex 的构建和操作成本要高得多,因此请尽可能使用普通的 QMutex。
使用场景
当一个公共函数A调用另一个公共函数B,它们都需要锁定同一个互斥锁。在这种情况下可以使用QRecursiveMutex。