线程的互斥和同步机制
互斥锁类(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() << "下载完成";
}
效果