QMutex — мьютексы в Qt


Мьютексы (mutex) обеспечивают взаимоисключающий доступ к ресурсам, гарантирующий то, что критическая секция будет обрабатываться только одним потоком. Поток, владеющий мьютексом, обладает эксклюзивным правом на использование ресурса, защищенного мьютексом, и другой поток не может завладеть уже занятым мьютексом.

Мьютексы можно образно сравнить с дверью душевой кабинки, способной разместить только одного человека. Как только в нее заходит один человек, то он закрывает дверь. И если после этого кто-то еще захочет воспользоваться этой душевой кабинкой, то он просто не сможет попасть в нее, так как дверь кабинки будет заперта. Как только кабинка освободится, ее дверь откроется и в нее сможет войти следующий желающий, войдя в нее он закроет за собой дверь, сделав ее недоступной для других, и т. д.

По этому принципу работает и мьютекс, только вместо людей выступают потоки, а вместо кабинок ресурсы. Его механизм реализован классом QMutex. Метод lock() класса QMutex производит блокировку ресурса. Для обратной операции существует метод unlock(), который открывает закрытый ресурс для других потоков.

Класс QMutex также содержит метод tryLock(). Этот метод можно использовать для того, чтобы проверить, заблокирован ресурс или нет. Этот метод не приостанавливает исполнение потока и возвращается немедленно, со значением false, если ресурс уже захвачен другим потоком, и не ожидает его освобождения. В случае успешного захвата ресурса этот метод вернет true, а это значит, что ресурс принадлежит потоку и он вправе распоряжаться им по своему усмотрению.

Давайте воспользуемся мьютексом для того, что бы реализовать класс с механизмом надежности использования в потоках (thread safety).


class ThreadSafeStringStack
{
private:
    QMutex	m_mutex;
    QStack<QString> m_stackString;
public:

    void push(const QString& str)
    {
        m_mutex.lock();
        m_stackString.push(str);
        m_mutex.unlock();
    }

    QString pop(const QString& str)
    {
        QMutexLocker locker (&m_mutex);
        return m_stackString.empty() ? QString() : m_stackString.pop();
    }
};

В классе ThreadSafeStringStack мы определяем два метода. Один из которых служит для помещения строк в стек — push(), а другой — рор(), для извлечения из стека. В секции private определены два атрибута: атрибут мьютекса m_mutex и атрибут стека строк. Не забывайте, что класс QStack&tl;T> не обладает механизмом надежности использования в потоках. Единственная возможность синхронизировать доступ к данным объекта класса QStack&tl;T> — это блокировать их каждый раз, как только кто-то получит к ним доступ, и разблокировать после завершения операции. В методе push() самой первой строкой производится вызов метода lock() из объекта мьютекса, который производит блокировку доступа к ресурсу и, после помещения строкового значения в стек, разблокирует его вызовом метода unlock(). Теперь этот метод могут вызвать несколько потоков одновременно, и это не приведет к порче данных.

В методе pop() мы используем объект класса QMutexLocker. Иногда очень удобно использовать именно этот класс. Для создания объекта этого класса в его конструктор необходимо передать указатель на объект мьютекса. В конструкторе этого класса сразу же производится блокировка ресурса, а в деструкторе— разблокировка. Это означает, что нам не нужно явно вызывать метод для разблокирования ресурса, как мы это делали в методе push(), потому что завершение метода приведет к разрушению этого объекта и произведет вызов его деструктора.

Примечание. Золотое правило: никогда не используйте мьютексы, если вы не уверенны в том, что это действительно необходимо. Бытует мнение, что классы, не обладающие надежностью для потока, — это плохо. Проблема, связанная с реализацией надежности для потока, заключается в том, что ее невозможно сделать без механизма блокировки. Блокировка ресурсов нужна для синхронизации, и ее применение может снизить эффективность работы класса, а значит, и приложения в целом. Представьте себе, каждый класс вашего приложения должен блокировать и разблокировать ресурсы, что резко бы снизило быстродействие вашей программы. Именно по этой причине не все классы Qt реализованы с механизмом надежности.

Читать далее: QWaitCondition - условные ожидания в Qt