面试问题详解十二: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
cpp 复制代码
#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
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
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() 如下:

cpp 复制代码
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()