QMutex

Автор: | 5 августа, 2019

Основной задачей является защита объекта или части кода доступа только для одного потока при выполнении. При обращении к защищенному коду поток ожидает завершение блокировки и переходит к выполнению, когда объект завершит свое выполнение и снимет блокировку. Такая возможность позволяет сохранять правильное последовательность выполнение поставленной задачи.

Из основных методов можно выделить несколько функции которые позволяют блокировать  lock() и снимать блокировку  unlock() объекта. Также можно проверить статус блокировки tryLock() который вернет false если объект заблокирован другим потоком. Мьютекс позволяет реализовывать поток безопасные функции (thread safety).

Рассмотрим небольшой пример который выводит в разных потоках последовательность чисел от 1 до 3 и от -3 до -1

Пример использования QMutex

// main
#include <QtCore>
#include "test.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int Num=0;
    QMutex Mutex;

    int *_n= &Num;
    QMutex *_m = &Mutex;

    test mThread1(_m, _n);
    test mThread2(_m, _n);

    // Выбор последовательности метода
    mThread1.method = 1;
    mThread2.method = 2;

    mThread1.start();
    mThread2.start();

   return a.exec();

}
// test.cpp
#include "test.h"

test::test(QMutex *m, int *n)
    :mutex(m),number(n)
{

}


void test::run()
{
        if(method == 1)
            method1();
       else
            method2();
}


void test::method1()
{
    mutex->lock();
   for (int i = 1; i <= 3; i++)
    {
      *number = i;
      qDebug()<<*number << "method1" << method;
    }
    mutex->unlock();
}

void test::method2()
{
    mutex->lock();
   for (int i = 3; i >= 1; i--)
    {
     *number = -i;
      qDebug()<<*number << "method2" << method;
    }
    mutex->unlock();
}
// test.h
#ifndef TEST_H
#define TEST_H

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

class test: public QThread
{
public:
    test(QMutex *mu, int *nu);
    void run();
    void method1();
    void method2();
    int method;

private:
    int *number;
    QMutex *mutex;
};


#endif // TEST_H

На выходе мы получим:

1 method1 1
2 method1 1
3 method1 1
-3 method2 2
-2 method2 2
-1 method2 2

Как видно сначала отработал вызываемый метод 1 а затем метод 2.

Метод mutex->lock() и  mutex->unlock() позволил выполнить функцию  последовательно в одном потоке без прерывания на выполнение в другом.

Если убрать данные методы, то выполнение будет не последовательное и на выходе получится примерно следующее:

1 method1 1
-3 method2 2
2 method1 1
-2 method2 2
3 method1 1
-1 method2 2

Для облегчения работы с методом QMutex существует класс QMutexLocker который упрощает блокировку и разблокировку.

Блокировка и разблокировка QMutex в сложных функциях и инструкциях или в коде обработки исключений подвержена ошибкам и трудна для отладки. QMutexLocker может использоваться в таких ситуациях, чтобы гарантировать, что состояние мьютекса всегда четко определено. QMutexLocker должен быть создан внутри функции, где QMutex должен быть заблокирован.

Разблокировка произойдет, когда у QMutexLocker будет вызван деструктор класса.

Воспользуемся прошлым примеров и немного изменим его код:

void test::method1()
{
   QMutexLocker locker(mutex);
   for (int i = 1; i <= 3; i++)
    {
       *number = i;
       qDebug()<<*number << "method1" << method;
    }
}

И объявим его метод в классе test.h

#include <QMutexLocker>

Для изменения последовательности вызова изменим:

    mThread1.method = 2;
    mThread2.method = 1;

Вывод изменится на:

-3 method2 2
-2 method2 2
-1 method2 2
1 method1 1
2 method1 1
3 method1 1