【QT】 Qt 中线程互斥锁与同步机制实战分析

线程的互斥和同步机制

互斥锁类(QMutex)

//锁定互斥锁,如果已经被其他线程锁住,当前线程会阻塞等待。
void lock()
//解锁互斥锁,让其他等待的线程可以访问。
void unlock()
//尝试锁定,如果锁被占用,返回 false;可以指定超时时间(毫秒),超时未获取锁也返回 false
bool tryLock(int timeout = 0)

示例:

#include <QMutex>
#include <QThread>
#include <QDebug>

QMutex mutex;
int sharedCounter = 0;

class WorkerThread : public QThread {
protected:
    void run() override {
        for (int i = 0; i < 5; ++i) {
            mutex.lock();              // 上锁
            sharedCounter++;           // 修改共享资源
            qDebug() << "Counter:" << sharedCounter << "Thread:" << QThread::currentThreadId();
            mutex.unlock();            // 解锁
            QThread::msleep(100);      // 模拟工作耗费时间
        }
    }
};

int main() {
    WorkerThread thread1, thread2;
    thread1.start();
    thread2.start();
    thread1.wait();
    thread2.wait();
    qDebug() << "最终计数为:" << sharedCounter;
    return 0;
}

便捷工具:QMutexLocker

自动管理锁的生命周期,构造时加锁,析构时解锁,避免忘记 unlock()

void run() override {
    for (int i = 0; i < 5; ++i) {
        QMutexLocker locker(&mutex);  // 自动加锁
        sharedCounter++;
        qDebug() << "Counter:" << sharedCounter;
        // locker 离开作用域时自动解锁
        QThread::msleep(100);
    }
}

读写锁类(QReadWriteLock)

适用场景:数据库查询(多读)、配置更新(少写)

//加读锁,允许多个线程同时读
void lockForRead()
//加写锁,独占访问,读和写都被阻塞
void lockForWrite()
//解锁(读锁或写锁)
void unlock()
//尝试加锁,带超时。
bool tryLockForRead(int timeout = 0) / tryLockForWrite(int timeout = 0)

示例:

#include <QReadWriteLock>
#include <QThread>
#include <QDebug>

QReadWriteLock rwLock;
QString sharedData = "Hello";

class ReaderThread : public QThread {
protected:
    void run() override {
        rwLock.lockForRead();
        qDebug() << "Read:" << sharedData << "Thread:" << QThread::currentThreadId();
        QThread::msleep(100);
        rwLock.unlock();
    }
};

class WriterThread : public QThread {
protected:
    void run() override {
        rwLock.lockForWrite();
        sharedData = "World";
        qDebug() << "Wrote:" << sharedData;
        rwLock.unlock();
    }
};

int main() {
    ReaderThread reader1, reader2;
    WriterThread writer;
    reader1.start();
    reader2.start();
    writer.start();
    reader1.wait();
    reader2.wait();
    writer.wait();
    return 0;
}

信号量类(QSemaphore)

作用:控制有限资源的访问数量,允许多个线程访问,但不超过指定数量。

适用场景:限制并发线程数、资源池管理

//P操作(-1):获取 n 个资源,如果不够就阻塞
void acquire(int n = 1)
//V操作(+1):释放 n 个资源,通知等待的线程
void release(int n = 1)
//返回当前可用资源数
int available()
//尝试获取,带超时。
bool tryAcquire(int n = 1, int timeout = 0)

示例:

#include <QSemaphore>
#include <QThread>
#include <QDebug>

QSemaphore sem(2);  // 最多 2 个线程同时运行

class WorkerThread : public QThread {
protected:
    void run() override {
        sem.acquire();  // 获取一个资源
        qDebug() << "Working:" << QThread::currentThreadId();
        QThread::msleep(500);
        qDebug() << "Done:" << QThread::currentThreadId();
        sem.release();  // 释放资源
    }
};

int main() {
    WorkerThread thread1, thread2, thread3;
    thread1.start();
    thread2.start();
    thread3.start();  // thread3 会等待,直到有一个线程释放
    thread1.wait();
    thread2.wait();
    thread3.wait();
    return 0;
}

条件变量类(QWaitCodition)

作用:让线程等待某个条件满足,由其他线程唤醒。

适用场景:生产者-消费者模型,线程间协作。

//等待条件,释放锁并阻塞,唤醒后重新加锁
void wait(QMutex *mutex)
//唤醒一个等待的线程
void wakeOne()
//唤醒所有等待的线程(惊群)
void wakeAll()

示例

#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QDebug>

QMutex mutex;
QWaitCondition condition;
bool dataReady = false;

class Producer : public QThread {
protected:
    void run() override {
        QThread::msleep(1000);  // 模拟生产
        mutex.lock();
        dataReady = true;
        condition.wakeAll();    // 通知消费者
        mutex.unlock();
    }
};

class Consumer : public QThread {
protected:
    void run() override {
        mutex.lock();
        if (!dataReady) {
            condition.wait(&mutex);  // 等待生产者
        }
        qDebug() << "Data ready:" << dataReady;
        mutex.unlock();
    }
};

int main() {
    Producer producer;
    Consumer consumer;
    consumer.start();
    producer.start();
    producer.wait();
    consumer.wait();
    return 0;
}

综合示例

需求:使用线程、互斥锁拟文件下载

UI 设计


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>
#include "downloader.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();   // 开始按钮
    void on_pushButton_2_clicked(); // 停止按钮
    void updateProgress(int value); // 更新进度条

private:
    void resetDownloadState();      // 重置下载状态

    Ui::MainWindow *ui;
    QThread *downloadThread;        // 下载线程
    Downloader *downloader;         // 下载对象
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 设置进度条显示格式
    ui->progressBar->setFormat("%p%"); // 显示百分
    //初始状态
    ui->pushButton_2->setEnabled(false);
    // 初始化线程和下载对象
    downloadThread = new QThread(this);
    downloader = new Downloader();
    downloader->moveToThread(downloadThread);

    connect(downloadThread, &QThread::started, downloader, &Downloader::startDownload);
    connect(downloader, &Downloader::progressUpdated, this, &MainWindow::updateProgress);
}


void MainWindow::resetDownloadState() {
    if (downloadThread->isRunning()) {
        downloader->stop();
        downloadThread->quit();
        downloadThread->wait();
    }
    downloader->resetStop(); // 重置停止标志
}
MainWindow::~MainWindow()
{
    if (downloadThread->isRunning()) {
        downloader->stop();
        downloadThread->quit();
        downloadThread->wait();
    }
    delete downloadThread; // 手动删除线程
    delete downloader;     // 手动删除下载对象
    delete ui;
}

//开始按钮
void MainWindow::on_pushButton_clicked()
{
    if(!downloadThread->isRunning())//如果线程未运行
    {
        resetDownloadState(); // 确保线程停止并重置状态
        downloadThread->start();//启动线程
        ui->pushButton->setEnabled(false);//防止多次启动线程
        ui->pushButton_2->setEnabled(true);
        ui->progressBar->setValue(0);//初始值为0
    }
}

//停止按钮
void MainWindow::on_pushButton_2_clicked()
{
    if(downloadThread->isRunning())
    {
        downloader->stop();//设置停止标志
        downloadThread->quit();
        downloadThread->wait();
        ui->pushButton->setEnabled(true);
        ui->pushButton_2->setEnabled(false);
    }
}

//主线程负责更新UI
void MainWindow::updateProgress(int value)
{
    ui->progressBar->setValue(value);
    if(value == 100)//如果下载完成
    {
        QMessageBox::information(this,"下载","下载完成");
        ui->pushButton->setEnabled(true);
        ui->pushButton_2->setEnabled(false);
    }
}

downloader.h

#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include <QObject>
#include <QMutex>

class Downloader : public QObject {
    Q_OBJECT
public:
    explicit Downloader(QObject *parent = nullptr);
    void stop();
    void resetStop(); // 重置停止标志

public slots:
    void startDownload();

signals:
    void progressUpdated(int value);

private:
    QMutex mutex;
    bool shouldStop = false;
    int progress = 0; // 保存当前进度
};

#endif // DOWNLOADER_H

downloader.cpp

#include "downloader.h"
#include <QDebug>
#include <Qthread>
Downloader::Downloader(QObject *parent) : QObject{parent}, shouldStop(false), progress(0) {}

void Downloader::stop() {
    QMutexLocker locker(&mutex);
    shouldStop = true;
}

void Downloader::resetStop() {
    QMutexLocker locker(&mutex);
    shouldStop = false;
}

void Downloader::startDownload() {
    while (progress < 100) {
        {
            QMutexLocker locker(&mutex);
            if (shouldStop) {
                qDebug() << "下载停止";
                return;
            }
        }
        progress++;
        emit progressUpdated(progress);
        QThread::msleep(100);
    }
    qDebug() << "下载完成";
}

效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值