Qt互斥QMutex,QMutexLocker

发帖记录。

Qt帮助

qt帮助写得很容易懂,但仅仅是个示例,跟实际运行还是有区别的。

QMutex mutex;
  int number = 6;

  void method1()
  {
      mutex.lock();
      number *= 5;
      number /= 4;
      mutex.unlock();
  }

  void method2()
  {
      mutex.lock();
      number *= 3;
      number /= 2;
      mutex.unlock();
  }

Then only one thread can modify number at any given time and the result is correct. 
This is a trivial example, of course, but applies to any other case where things
need to happen in a particular sequence.
When you call lock() in a thread, other threads that try to call lock() in the same 
place will block until the thread that got the lock calls unlock(). A non-blocking 
alternative to lock() is tryLock().

跟网上大多代码一样,看起来清晰,但不能直接这样用。上面写了,这是要运行在两个线程当中的。所以并不是两个方法调用一个变量。

我做了一个小例子想验证一下。

思路

界面上读入一个整型数字。

线程1执行:加1,再乘2。线程2执行:乘2,再加1。

每一步都sleep一会儿,输出结果分析执行过程。

界面

很简单就是一个按钮用来启动线程,把执行的结果加载到列表中观察。

 clear按钮用来清空列表,上面的文本框用来输入数字,随便就好。

代码结构

 见名知意。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <thread1.h>
#include <thread2.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();

    //响应线程发来的消息,用于更新界面
    void on_Update(QString s);

private:
    Ui::MainWindow *ui;

    //运行于主线程中的变量,不能写在按钮槽中,否则不能滞留内存,达不到效果
    int m_i;

    //互斥变量,两个线程要公用一个才有效果,这里没有用*号
    QMutex m_mutex;

};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //从文本框读入数字
    m_i = ui->lineEdit->text().toInt();

    //创建子线程,线程重写了构造函数,用于传送变量,我使用了指针,希望指向同一内存
    Thread1 *thd1 = new Thread1(&m_i);
    Thread2 *thd2 = new Thread2(&m_i);
    
    //绑定信号与槽,用于子线程向主线程发结果更新界面
    connect(thd1, SIGNAL(sigUpdate(QString)), this, SLOT(on_Update(QString)));
    connect(thd2, SIGNAL(sigUpdate(QString)), this, SLOT(on_Update(QString)));
    
    //把互斥变量的地址传给子线程,我希望确实操作的是同一个对象
    thd1->m_mutex = &m_mutex;
    thd2->m_mutex = &m_mutex;

    thd1->start();
    thd2->start();

    ui->textEdit->append("---------------------------");
}

void MainWindow::on_Update(QString s)
{
    ui->textEdit->append(s);
}

void MainWindow::on_pushButton_2_clicked()
{
    ui->textEdit->clear();
}

thread1.h

#ifndef THREAD1_H
#define THREAD1_H

#include <QThread>
#include    <QMutex>

class Thread1 : public QThread
{
    Q_OBJECT
public:
    explicit Thread1(int *i);
    QMutex *m_mutex;//子线程中声明互斥变量的指针,跟主线程中声明不一样

protected:
    void run();
    int *m_i;

private:

signals:
    //定义信号,用于向主线程发送结果,更新界面
    void sigUpdate(QString s);
};

#endif // THREAD1_H

thread1.cpp

#include "thread1.h"

Thread1::Thread1(int *i)
{
    m_i = i;
}

void Thread1::run()
{
    (*m_mutex).lock();//对象指针也可以这样写:m_mutex->lock();

    *m_i += 1;

    //发信号,用于向主线程发送结果,更新界面
    emit sigUpdate("Thread1: i + 1 = " + QString::number(*m_i));

    msleep(500);

    *m_i *= 2;

    //发信号,用于向主线程发送结果,更新界面
    emit sigUpdate("Thread1: i * 2 = " + QString::number(*m_i));

    msleep(500);

    (*m_mutex).unlock();//对象指针也可以这样写:m_mutex->unlock();
}

thread2.h

#ifndef THREAD2_H
#define THREAD2_H

#include <thread1.h>

class Thread2 : public Thread1//直接从thread1继承了一个
{
    Q_OBJECT
public:
    explicit Thread2(int *i);//因为thread1重写的构造函数,这里必须显式继承构造函数
protected:
    void run();
};

#endif // THREAD2_H

thread2.cpp

#include "thread2.h"

Thread2::Thread2(int *i):Thread1(i)//显式继承构造函数,注意参数传递方式
{}
void Thread2::run()
{
    (*m_mutex).lock();//对象指针也可以这样写:m_mutex->lock();

    *m_i *= 2;

    //发信号,用于向主线程发送结果,更新界面
    emit sigUpdate("Thread2: i * 2 = " + QString::number(*m_i));  
  
    msleep(500);

    *m_i += 1;

    //发信号,用于向主线程发送结果,更新界面
    emit sigUpdate("Thread2: i + 1 = " + QString::number(*m_i));

    msleep(500);

    (*m_mutex).unlock();//对象指针也可以这样写:m_mutex->unlock();
}

验证结果

首先注释掉代码中所有(*m_mutex).lock();和(*m_mutex).unlock();运行之后,文本框输入数字“1”,执行begin按钮。

 很明显,两个线程异步执行,变量i的数值没有规律。

接着,去掉代码中所有(*m_mutex).lock();和(*m_mutex).unlock();的注释,再运行。

启用QMutex之后,两个线程不会互相穿插执行了。要么执行直到完毕,要么等待另一个线程完成再开始。

这就是互斥的效果。当两个线程共享内存时,可以实现独占的效果。

QMutexLocker

看看qt帮助就知道。

Locking and unlocking a QMutex in complex functions and statements or in exception handling code is error-prone and difficult to debug. QMutexLocker can be used in such situations to ensure that the state of the mutex is always well-defined.

QMutexLocker should be created within a function where a QMutex needs to be locked. The mutex is locked when QMutexLocker is created. You can unlock and relock the mutex with unlock() and relock(). If locked, the mutex will be unlocked when the QMutexLocker is destroyed.

This example function will get more complicated as it is developed, which increases the likelihood that errors will occur.

Using QMutexLocker greatly simplifies the code, and makes it more readable:...

Now, the mutex will always be unlocked when the QMutexLocker object is destroyed (when the function returns since locker is an auto variable).

大概意思就是,遇到使用Mutex比较麻烦复杂的时候,使用QMutexLocker可以简化代码,方法就是把QMutexLocker放在局部使用,执行时启用lock,不用时自动unlock。

以上面的thread1.cpp为例,可以这样写:

#include "thread1.h"

Thread1::Thread1(int *i)
{
    m_i = i;
}

void Thread1::run()
{    
    /* 等价于:(*m_mutex).lock();
     * 对象引用使用*号时,相当于引用对象本身,使用成员函数使用“.”号。
     * 对象引用不带*号时,相当于引用对象地址,使用成员函数使用“->”号。推荐写法。
     */
//    m_mutex->lock();

    //加上这句,就不再使用lock和unlock,自动实现
    QMutexLocker locker(m_mutex);

    *m_i += 1;
    emit sigUpdate("Thread1: i + 1 = " + QString::number(*m_i));
    msleep(500);
    *m_i *= 2;
    emit sigUpdate("Thread1: i * 2 = " + QString::number(*m_i));
    msleep(500);

    /* 等价于:(*m_mutex).lock();
     * 对象引用使用*号时,相当于引用对象本身,使用成员函数使用“.”号。
     * 对象引用不带*号时,相当于引用对象地址,使用成员函数使用“->”号。推荐写法。
     */
//    m_mutex->unlock();
}

一定要注意QMutex变量的用法,使用lock和unlock时可以直接打“.”号,我使用“->”号是因为用了指针,因为要想达到互斥的效果,必须两个线程应用的是一个QMutex变量,所以我写的两个线程中,都定义成了指针,赋值时指向了主线程的变量实体,所以在应用成员函数时,使用了“->”号。

而QMutexLocker的用法,也是同理,传递给Locker构造函数要使用地址,也就是带“&”号。但是由于这句代码写在子线程中,而子线程中我用了指针,所以反而不用“&”号了。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值