面试问题详解十二:Qt 多线程同步:QMutex讲解
在多线程编程中,当多个线程访问共享资源(如全局变量、文件、数据库连接等)时,如果不加控制,会导致 数据竞争(Data Race) 和 不确定的程序行为。为了保证线程安全,我们需要使用同步原语来对共享资源进行保护。
Qt 提供了多种线程同步机制,其中最基本也是最常用的就是 互斥锁(
QMutex
)。
一、QMutex 是什么?
QMutex
是 Qt 提供的互斥锁类,用于在线程之间同步访问共享资源。它确保在同一时间只有一个线程可以进入临界区(访问共享资源的代码块)。
常见用法:
lock()
:锁定互斥量。如果已经被其他线程锁定,则当前线程会阻塞等待。unlock()
:释放互斥量,使其他等待的线程可以继续。tryLock()
:尝试锁定互斥量,如果无法立即锁定,不会阻塞,直接返回false
。
二、使用 QMutex 的基本示例
下面是一个完整的例子:我们创建两个线程去同时增加一个共享的整数变量 counter
。通过 QMutex
来保护这个变量,避免并发访问的问题。
1. 项目结构(文件一览)
main.cpp
:主函数worker.h / worker.cpp
:线程类定义与实现
2. 代码详解
worker.h
#ifndef WORKER_H
#define WORKER_H#include <QThread>
#include <QMutex>class Worker : public QThread
{
public:Worker(int id, int* counter, QMutex* mutex);void run() override;private:int m_id;int* m_counter;QMutex* m_mutex;
};#endif // WORKER_H
worker.cpp
#include "worker.h"
#include <QDebug>Worker::Worker(int id, int* counter, QMutex* mutex): m_id(id), m_counter(counter), m_mutex(mutex)
{}void Worker::run()
{for (int i = 0; i < 5; ++i) {m_mutex->lock(); // 加锁,保护共享变量(*m_counter)++;qDebug() << "Thread" << m_id << "incremented counter to" << *m_counter;m_mutex->unlock(); // 解锁msleep(100); // 模拟耗时操作}
}
main.cpp
#include <QCoreApplication>
#include "worker.h"
#include <QMutex>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);int counter = 0; // 共享资源QMutex mutex; // 互斥锁保护共享资源Worker thread1(1, &counter, &mutex);Worker thread2(2, &counter, &mutex);thread1.start();thread2.start();thread1.wait(); // 等待线程1完成thread2.wait(); // 等待线程2完成qDebug() << "Final counter value:" << counter;return 0;
}
三、运行效果
程序运行后,两条线程会交替输出增加 counter
的值:
Thread 1 incremented counter to 1
Thread 2 incremented counter to 2
Thread 1 incremented counter to 3
Thread 2 incremented counter to 4
...
Final counter value: 10
由于使用了 QMutex
,即使两个线程同时运行,counter
的值也不会出现跳变或错误,确保了线程安全。
四、QMutex 的常见用法与注意事项
✅ 推荐用法:使用 QMutexLocker
为了防止忘记调用 unlock()
导致死锁,Qt 提供了一个辅助类 QMutexLocker
,它利用 RAII(资源获取即初始化)原则自动管理锁的释放。
修改 Worker::run()
如下:
void Worker::run()
{for (int i = 0; i < 5; ++i) {QMutexLocker locker(m_mutex); // 自动加锁和解锁(*m_counter)++;qDebug() << "Thread" << m_id << "incremented counter to" << *m_counter;msleep(100); // 解锁后执行}
}
只要 locker
对象超出作用域,锁就会自动释放,不再需要手动调用 unlock()
。
五、总结
- QMutex 是保护共享资源最常用的同步机制,适用于绝大多数线程同步场景。
- 加锁时务必注意解锁,否则容易造成死锁。
- 推荐使用
QMutexLocker
管理互斥锁,避免忘记unlock()
。