> 文档中心 > Qt互斥锁(QMutex)的使用、QMutexLocker的使用(含源码+注释)

Qt互斥锁(QMutex)的使用、QMutexLocker的使用(含源码+注释)


一、QMutexLocker和QMutex实现示例图

下图为检测QMutexLocker是否上锁成功的示例图(两个线程使用同一个QMutex),源码在文章第四节(源码含详细注释)。
在这里插入图片描述
下图为不同QMutex运行时的效果(该图表明两个线程无关,并非sleep影响了另一个线程的运行)
在这里插入图片描述

二、QMutex和QMutexLocker的关系(个人理解)

互斥锁(QMutex)在使用时需要在进入和结束的时候使用对应的函数锁定和解锁。在简单的程序中还好,但是在结构复杂的程序中因为需要手动锁定和解锁,很容易忽略细节而出现问题,于是为了应对这种情况QMutexLocker便诞生了(为了简化简化互斥锁的锁定和解锁)。
QMutexLocker通常创建为局部变量,QMutexLocker在创建时传入一个并未锁定(若是锁定可用relock重新锁定或unlock解锁)的QMutex指针变量,并且会将QMutex变量锁定,在释放时会将QMutex变量解锁。(QMutexLocker创建时将传入的QMutex锁定,释放时将传入的QMutex解锁)

三、QMutex使用和QMutexLocker使用

1.QMutex的使用

void CThread::run(){ //互斥锁锁定 m_mutex->lock(); //输出当前线程的线程ID qDebug() << QThread::currentThreadId(); //互斥锁解锁 m_mutex->unlock();}

2.QMutexLocker的使用

void CThread::run(){ //创建QMutexLocker的局部变量,并将类中互斥锁指针传入(此处互斥锁被locker锁定) QMutexLocker locker(m_mutex); qDebug() << QThread::currentThreadId(); //当locker作用域结束locker将互斥锁解锁}

通过1、2的代码比较,我们会发现QMutexLocker的代码中没有手动调用锁定和解锁,由此可看出QMutexLocker简化了互斥锁的锁定和解锁

四、检验QMutexLocker是否将传入的互斥锁锁定

1.操作解释

  1. 使用两种实现方法完全不同线程测试
  2. 两个线程使用同一个互斥锁
  3. 一个线程使用QMutexLocker一个线程单纯使用QMutex

2.CMoveFuncClass(使用moveToThread实现,使用QMutexLocker)

CMoveFuncClass.h

#ifndef CMOVEFUNCCLASS_H#define CMOVEFUNCCLASS_H#include #include class CMoveFuncClass : public QObject{    Q_OBJECTpublic:    explicit CMoveFuncClass(QObject *parent = nullptr);    ~CMoveFuncClass();    void setMutex(QMutex *mutex);public slots:    void doSomething();private:    QMutex * m_mutex;   //定义一个互斥锁变量};#endif // CMOVEFUNCCLASS_H

CMoveFuncClass.cpp

#include "CMoveFuncClass.h"#include #include CMoveFuncClass::CMoveFuncClass(QObject *parent)    : QObject(parent){}CMoveFuncClass::~CMoveFuncClass(){}void CMoveFuncClass::doSomething(){    //创建QMutexLocker的局部变量,并将类中互斥锁指针传入(此处互斥锁被locker锁定)    QMutexLocker locker(m_mutex);    qDebug() << "我的实现方法为moveToThread" <<"开始3秒睡眠" << "使用QMutexLocker";    qDebug() << "线程ID:" << QThread::currentThreadId();    QThread::sleep(3);  //设置线程睡眠3秒(单位为秒)    qDebug() << "我的实现方法为moveToThread" <<"线程运行完成,结束睡眠\n\n";    //当locker作用域结束locker将互斥锁解锁}void CMoveFuncClass::setMutex(QMutex *mutex){    m_mutex = mutex;}

3.CThread类(继承QThread实现,单纯使用QMutex)

CThread.h

#ifndef CTHREAD_H#define CTHREAD_H#include #include #include #include class CThread : public QThread{    Q_OBJECTpublic:    explicit CThread(QObject *parent = nullptr);    ~CThread();    void run();    void setMutex(QMutex *mutex);private:    QMutex *     m_mutex;     //定义一个线程锁变量};#endif // CTHREAD_H

CThread.cpp

#include "CThread.h"#include CThread::CThread(QObject *parent)    : QThread(parent){}CThread::~CThread(){}void CThread::run(){    //互斥锁上锁    m_mutex->lock();    qDebug() << "我的实现方法为继承QThread" << "开始3秒睡眠" << "单纯使用QMutex";    qDebug() << "线程ID:" << QThread::currentThreadId();    QThread::sleep(3);  //设置线程睡眠3秒(单位为秒)    qDebug() << "我的实现方法为继承QThread" <<"线程运行完成,结束睡眠";    //互斥锁解锁    m_mutex->unlock();}void CThread::setMutex(QMutex *mutex){    m_mutex = mutex;}

4.CMainWindow调用类

CMainWindow.h

#ifndef CMAINWINDOW_H#define CMAINWINDOW_H#include #include "CThread.h"#include "CMoveFuncClass.h"namespace Ui {class CMainWindow;}class CMainWindow : public QMainWindow{    Q_OBJECTpublic:    explicit CMainWindow(QWidget *parent = 0);    ~CMainWindow();signals:    void startMoveThread();private slots:    void on_startBtn_clicked();//触发方法二函数的信号private:    Ui::CMainWindow *ui;    CThread  *m_cThread;//方法一指针    CMoveFuncClass  *m_moveFunc;//方法二指针    QThread  *m_thread;//方法二所移至的线程指针    QMutex   *m_mutex;//两个线程使用的线程锁};#endif // CMAINWINDOW_H

CMainWindow.cpp

#include "CMainWindow.h"#include "ui_CMainWindow.h"#include CMainWindow::CMainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::CMainWindow){    ui->setupUi(this);    /方法一///    //new出CThread对象    m_cThread = new CThread;    /方法二///    //new一个moveToThread的接收线程并启动    m_thread = new QThread;    //new出CMoveFuncClass对象    m_thread->start();  //一定记得启动,否则运行不了    m_moveFunc = new CMoveFuncClass;    //连接相应信号槽    connect(this, &CMainWindow::startMoveThread, m_moveFunc, &CMoveFuncClass::doSomething);    connect(m_thread, &QThread::finished, m_moveFunc, &QObject::deleteLater);    //将对象移至线程    m_moveFunc->moveToThread(m_thread);//创建线程共用的互斥锁    m_mutex = new QMutex;    //下方为m_mutex的地方更改为new QMutex,则能实现第一节,第二张图的效果    m_cThread->setMutex(m_mutex);    m_moveFunc->setMutex(m_mutex);}CMainWindow::~CMainWindow(){    delete m_mutex;    delete m_moveFunc;    m_thread->exit();    m_thread->wait(1);    delete m_thread;    m_cThread->exit();    m_cThread->wait(1);    delete m_cThread;    delete ui;}void CMainWindow::on_startBtn_clicked(){    //通过start启动方法一线程    m_cThread->start();    //发送信号启动方法二线程    emit startMoveThread();}

运行上方的代码(第一节,第一张效果图)可看出,使用QMutexLocker的线程首先运行,且代码中无锁定和解锁的操作,但另外一个线程依然等该线程运行完成后运行,由此可看出,使用QMutexLocker是实现了互斥锁的锁定和解锁的。

总结

QMutexLocker提供的简化互斥锁锁定和解锁的机制在很多时候时蛮方便的,在使用互斥锁的地方使用QMutexLocker会减去许多安全隐患;不过在多线程循环输出ABC的时候好像就不适合该方法。所以使用类似的类还得按情况而定

相关文档

启动QThread线程的两种方法(含源码+注释)
Qt互斥锁(QMutex)、条件变量(QWaitCondition)讲解+QMutex实现多线程循环输出ABC(含源码+注释)
QSemaphore的使用+QSemaphore实现循环输出ABC(含源码+注释)
QRunnable线程、QThreadPool(线程池)的使用(含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除