Для чего нужен mutex

[C++] часть 2: МЬЮТЕКС. Пишем наш первый код для многопоточной среды

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

В прошлой статье мы разобрались с тем, что такое конкурентность/параллелизм и зачем нужна синхронизация. Настала пора изучить примитивы синхронизации, которые предлагает нам стандартная библиотека шаблонов C++.

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Что такое мьютекс?

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

Синтаксис

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Как создать потокобезопасную очередь

Разберёмся, как реализовать простейшие потокобезопасные очереди, то есть очереди с безопасным доступом для потоков.
В библиотеке стандартных шаблонов уже есть готовая очередь ( rawQueue ). Наша реализация будет предполагать: а) извлечение и удаление целочисленного значения из начала очереди и б) добавление нового в конец очереди. И всё это при обеспечении потокобезопасности.

Сначала выясним, почему и как эти две операции могут создавать проблемы для многопоточности.

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

Обратите внимание:

Lock guard и парадигма RAII

У нас две большие проблемы с этим простым мьютексом:

Посмотрим теперь, как можно изменить нашу потокобезопасную очередь threadSafe_queue (на этот раз обращаем внимание на то, где освобождается мьютекс).

Unique lock, дающий свободу

Как только владение мьютексом получено (благодаря std::lock_guard ), он может быть освобождён. std::unique_lock действует в схожей манере плюс делает возможным многократный захват и освобождение (всегда в таком порядке) мьютекса, используя те же преимущества безопасности парадигмы RAII.

Когда использовать?

Общий мьютекс + общий захват дают больше читателей

std::mutex — это мьютекс, которым одномоментно может владеть только один поток. Однако это ограничение не всегда обязательно. Например, потоки могут одновременно и безопасно читать одни и те же общие данные. Просто читать, не производя с ними никаких изменений. Но в случае с доступом к записи только записывающий поток может иметь доступ к данным.

Начиная с C++17, std::shared_mutex формирует доступ двух типов:

Синтаксис

Scoped lock, дающий больше мьютексов (и без клинча)

Краткая история взаимоблокировки:

Поток A хочет увести 200$ с банковского счёта Жеки на счёт Бяки в виде одной атомарной операции. Он начинает с того, что захватывает мьютекс, защищающий счёт Жеки, чтобы изъять деньги, а затем пытается захватить счёт Бяки.
В то же время поток B хочет увести 100$ со счёта Бяки на счёт Жеки. Он получает захват счёта Бяки, чтобы изъять деньги и попытаться захватить счёт Жеки. Оба потока блокируются, уснув в ожидании друг друга.

std::scoped_lock одновременно захватывают (а затем освобождают) все мьютексы, передаваемые в качестве аргумента, по принципу «всё или ничего»: если хотя бы один захват выбрасывает исключение, освобождаются все уже захваченные мьютексы.

Заключение

Если вы вдруг запутались в этом ворохе новой информации:

До встречи в следующей статье, в которой речь пойдёт о condition_variable и вы узнаете, как синхронизировать потоки!

Источник

[C++] часть 2: МЬЮТЕКС. Пишем наш первый код для многопоточной среды

Научитесь создавать код для использования в многопоточной среде с помощью реализации потокобезопасной очереди

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Nov 30, 2019 · 8 min read

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

В прошлой статье мы разобрались с тем, что такое конкурентность/параллелизм и зачем нужна синхронизация. Настала пора изучить примитивы синхронизации, которые предлагает нам стандартная библиотека шаблонов C++.

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Что такое мьютекс?

Мьютекс (англ. mutex, от mut ual ex clusion — «взаимное исключение») — это базовый механизм синхронизации. Он предназначен для организации взаимоисключающего доступа к общим данным для нескольких потоков с использованием барьеров памяти (для простоты можно считать мьютекс дверью, ведущей к общим данным).

Синтаксис

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Как создать потокобезопасную очередь

Разберёмся, как реализовать простейшие потокобезопасные очереди, то есть очереди с безопасным доступом для потоков.
В библиотеке стандартных шаблонов уже есть готовая очередь ( rawQueue ). Наша реализация будет предполагать: а) извлечение и удаление целочисленного значения из начала очереди и б) добавление нового в конец очереди. И всё это при обеспечении потокобезопасности.

Сначала выясним, почему и как эти две операции могут создавать проблемы для многопоточности.

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

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

Обратите внимание:

Lock guard и парадигма RAII

У нас две большие проблемы с этим простым мьютексом:

Посмотрим теперь, как можно изменить нашу потокобезопасную очередь threadSafe_queue (на этот раз обращаем внимание на то, где освобождается мьютекс).

Unique lock, дающий свободу

Как только владение мьютексом получено (благодаря std::lock_guard ), он может быть освобождён. std::unique_lock действует в схожей манере плюс делает возможным многократный захват и освобождение (всегда в таком порядке) мьютекса, используя те же преимущества безопасности парадигмы RAII.

Когда использовать?

Общий мьютекс + общий захват дают больше читателей

std::mutex — это мьютекс, которым одномоментно может владеть только один поток. Однако это ограничение не всегда обязательно. Например, потоки могут одновременно и безопасно читать одни и те же общие данные. Просто читать, не производя с ними никаких изменений. Но в случае с доступом к записи только записывающий поток может иметь доступ к данным.

Начиная с C++17, std::shared_mutex формирует доступ двух типов:

Синтаксис

Scoped lock, дающий больше мьютексов (и без клинча)

Краткая история взаимоблокировки:

Поток A хочет увести 200$ с банковского счёта Жеки на счёт Бяки в виде одной атомарной операции. Он начинает с того, что захватывает мьютекс, защищающий счёт Жеки, чтобы изъять деньги, а затем пытается захватить счёт Бяки.
В то же время поток B хочет увести 100$ со счёта Бяки на счёт Жеки. Он получает захват счёта Бяки, чтобы изъять деньги и попытаться захватить счёт Жеки. Оба потока блокируются, уснув в ожидании друг друга.

std::scoped_lock одновременно захватывают (а затем освобождают) все мьютексы, передаваемые в качестве аргумента, по принципу « всё или ничего»: если хотя бы один захват выбрасывает исключение, освобождаются все уже захваченные мьютексы.

Заключение

Если вы вдруг запутались в этом ворохе новой информации:

До встречи в следующей статье, в которой речь пойдёт о condition_variable и вы узнаете, как синхронизировать потоки!

Источник

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Мьютекс

Принимает два значенения:

Задача мьютекса — защита объекта от доступа к нему других потоков, отличных от того, который завладел мьютексом. В каждый конкретный момент только один поток может владеть объектом, защищённым мьютексом. Если другому потоку будет нужен доступ к переменной, защищённой мьютексом, то этот поток засыпает до тех пор, пока мьютекс не будет освобождён.

Содержание

Мьютекс в операционных системах

Использование mutex в Windows

Использование mutex в Linux

(Наследуется от Object.)

GetLifetimeService()Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)GetType()Возвращает объект класса Type для текущего экземпляра. (Наследуется от Object.)InitializeLifetimeService()Возвращает объект обслуживания во время существования для управления политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)OpenExisting(String)Открывает указанный именованный мьютекс, если он уже существует.OpenExisting(String, MutexRights)Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа.ReleaseMutex()Освобождает объект Mutex один раз.SetAccessControl(MutexSecurity)Задает безопасность управления доступом для именованного системного мьютекса.ToString()Возвращает строковое представление текущего объекта.

(Наследуется от Object.)

TryOpenExisting(String, Mutex)Открывает указанный именованный мьютекс, если он уже существует, и возвращает значение, указывающее, успешно ли выполнена операция.TryOpenExisting(String, MutexRights, Mutex)Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа, и возвращает значение, указывающее, успешно ли выполнена операция.WaitOne()Блокирует текущий поток до получения сигнала объектом WaitHandle.(Наследуется от WaitHandle.)WaitOne(Int32)Блокирует текущий поток до получения текущим дескриптором WaitHandle сигнала, используя 32-разрядное целое число со знаком для указания интервала времени в миллисекундах.(Наследуется от WaitHandle.)WaitOne(Int32, Boolean)Блокирует текущий поток до получения сигнала текущим объектом WaitHandle, используя 32-разрядное целое число со знаком для задания периода времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)WaitOne(TimeSpan)Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для указания интервала времени.(Наследуется от WaitHandle.)WaitOne(TimeSpan, Boolean)Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для задания интервала времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)

Свойства

Структура управления мьютексом

Каждый мьютекс ассоциируется со структурой управления:

Элементы структуры мьютекса

wait_queueОчередь задач, ожидающих освобождение мьютекса
mutex_queueЭлемент списка заблокированных задачей мьютексов
ock_mutex_queueСистемная очередь заблокированных мьютексов
attrАтрибут (тип обхода инверсии приоритетов) мьютекса
holderУказатель на TCB(Task Control Block) задачи, блокирующей мьютекс
ceil_priorityМаксимальный приоритет из задач, которые могут использовать ресурс (требуется для протокола увеличения приоритета)
cntЗарезервировано
id_mutexПоле идентификации объекта как мьютекса

Синхронизация файловых операций Mutex

Задача сводится к тому как заставить две программы работать с одним файлом, когда одна программа может писать, а вторая должна читать. Задача как избежать конфликтов при данной ситуации. Создадим два проекта, как Win32 console, один с именем WriteData, а другой с именем ReadData в каталоге TestMutex. Так будет и в прилагаемом проекте.

Основа программы функция CreateMutex :

Запустите одновременно две программы. Программа чтения будет ждать пока программа записи не разрешит доступ. Вот примерно так как на экране.

Для чего нужен mutex. Смотреть фото Для чего нужен mutex. Смотреть картинку Для чего нужен mutex. Картинка про Для чего нужен mutex. Фото Для чего нужен mutex

Разница между мьютексом и Семафор (Операционные Системы)

Семафор работает в режиме пользователя и блокирует задачи. Мьютекс работает в режиме ядра и блокирует сам ресурс. На время выполнения мьютекса, задача, которая захватила мьютекс получает максимальный приоритет, соответственно, если задача выполняет какие-либо действия внутри семафора, то более высокоприоритетная задача может прервать его выполнение, если задача захватила мьютекс, то прекратить выполнение пока она освободит мьютекс, не может никакая другая задача.

Источник

Обзор примитивов синхронизации — mutex и cond

Синхронизация нужна в любой малтитредной программе. (Если, конечно, она не состоит из локлесс алгоритмов на 100%, что вряд ли). Будь то приложение или компонента ядра современной операционной системы.

Меня всё нижесказанное, конечно, больше волнует с точки зрения разработки ядра ОС. Но почти всё применимо и к пользовательскому коду.

Кстати, ядра старых ОС в примитивах синхронизации не нуждались, поскольку преемптивной мультизадачности внутри ядра в старые добрые времена не было. (Уж за Юникс 7-й версии я отвечаю. Не было.) Точнее, единственным методом синхронизации был запрет прерываний. Но об этом позже.

Сначала перечислим героев. Мне известны следующие примитивы синхронизации:

User/kernel mode: mutex+cond, sema, enter/leave critical section.
Kernel only: spinlock, управление прерываниями.

Зачем всё это нужно, читатель, наверное, знает, но всё же уточним.

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

Простой пример. Список.

Вставляем элемент в список.

Применим мьютекс — mutually exclusive lock. Этот замок запрещает параллельное исполнение запертого им кода — если одна нить начала его исполнять, вторая будет ждать на входе до тех пор, пока первая не закончит.

Теперь хорошо. (Ну, не очень хорошо, если у нас более ста процессоров и участок кода у них популярен, но это совсем отдельный разговор.)

Что происходит? Нить А делает вызов mutex_lock для мьютекса list->mutex. Который, очевидно, принадлежит списку, который мы хотим поменять, и защищает доступ именно к нему. Он не заперт, нить А запирает мьютекс (теперь он знает, что заперт, и знает, кто его запер) и продолжает работу. Если теперь нить Б попробует войти в тот же регион кода (или другой, защищённый тем же мьютексом — например, в функции удаления элемента списка), то второй раз запереть запертый мьютекс не получится. Нить Б будет ждать, пока нить А не вызовет mutex_unlock.

Кстати, если мы с вами — ядерные разработчики, то важно понимать ещё одно, неинтересное для прикладного программиста свойство мьютекса (как и всех «тяжеловесных» примитивов синхронизации) — если мы пытаемся запереть мьютекс, который уже заперт другой нитью, мы не просто ждём — нашу нить «снимут с процессора», произойдёт переключение контекста. Это ценно, потому что позволяет куда более эффективно загружать процессор, но есть и проблемы. Внутри обработчика прерываний, например, такие примитивы применять нельзя совсем, потому что переключение контекста внутри прерывания запрещено и грозит изрядным раздраем в работе системы. Но, наверное, об этом надо будет написать отдельно.

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

Рассмотрим функции alloc_mem и free_mem:

Что здесь происходит? Всё банально. В функции аллокации памяти мы смотрим на глобальный счётчик свободной памяти. Если пусто, свободной памяти нет, ждём пока кто-то не освободит память — вызываем wait_cond, который нас приостанавливает, пока кто-то не просигналит — готово, память освободили.

Это, конечно, функция free_mem() — она возвращает память в кучу, увеличивает счётчик свободной памяти и вызывает signal_cond — сообщает страждущим, что память есть. Тот, кто спал внутри wait_cond, «проснётся» после такого сигнала, проверит что да, память есть, и выделит её. Всё верно?

Ну, нет, конечно. Если функцию alloc_mem вызовут две нити сразу, то будет беда — одна из них получит сигнал первой, проснётся, убедится, что свободная память есть, и тут вдруг шедулер возьми да сними её с процессора. И дай проснуться второй такой же нити. Вторая нить проснётся, тоже увидит, что память есть, заберёт её и закончится. Просыпается мафия первая нить, и у неё всё плохо. Только что она проверила переменную free_mem, убедилась, что всё есть, и вот — никакой свободной памяти в пуле не находится. Беда.

Но, вроде бы, мы же знаем ответ? Добавим mutex!

Так хорошо? Нет. Освобождение памяти не случится — функция alloc_mem() его заперла, заснула на ожидании cond, и никто больше в мьютекс войти не может, и никто не освободит память, и не просигналит.

Беда. Но ладно же, мы знаем, что делать! Перед тем, как заснуть на ожидании cond, мы отопрём mutex, и позволим другим войти в free и вернуть нам память. Вот так:

По комментарию вы уже видите, что опять не слава богу. Что теперь? А теперь есть щёлочка, тонкая линия между моментом, когда мы проснулись и вышли из функции wait_cond, получив от free_mem сигнал об освобождении памяти, и захватом мьютекса. В этот момент мьютекс не взят, и другие нити опять могут нас опередить и набезобразить. Именно по этой причине функция wait_cond выглядит несколько иначе:

Работает это вот как: функция принимает на вход conditional variable, которая передаст нам сигнал «проснуться», и запертый мьютекс:

Функция wait_cond отопрёт мьютекс, во-первых, самостоятельно, а во-вторых сделает это атомарно по отношению к переходу в спящее состояние. То есть нить, входящая в wait_cond сначала заснёт, а потом, не прерывая сна, отопрёт мьютекс. И наоборот, просыпаясь, она сначала захватит мьютекс, а потом проснётся и продолжит работу. (Это требует от кода переключения нитей изрядной хитрости, постараюсь рассказать об этом в одной из следующих заметок.)

Только такая семантика обеспечивает 100% консистентность и отсутствие «гонок» — race conditions.

Отметим, что код функции free у нас получился вполне правильный:

Только с учётом вышесказанного надо понимать, что хотя формально мы пробуждаем аллокатор на строке 4, реально проснётся он после исполнения строки 5, потому что до этого момента он не в состоянии захватить мьютекс.

К сказанному, наверное, имеет смысл добавить, что реальная функция signal_cond пробуждает не все ожидающие потоки, а только один (обычно — с наивысшим приоритетом), так что ситуация в приведённом примере несколько проще и сложнее одновременно. Проще потому что уже внутри сигнализации встроен механизм, который после одного free пробудит только один alloc, а сложнее потому, что реально это ничего не решает — мы не знаем, подойдёт ли данному alloc-у освобождённый участок, так что надо вместо signal_cond применить broadcast_cond, который таки пробудит всех страждущих, дав им возможность в честной драке определиться, кому достанется ресурс.

Посмотреть на фактическую реализацию этих примитивов можно здесь:

В следующей серии — sema семафор, который в одиночку заменяет и mutex, и cond. Практически без ансамбля.

Источник

Многопоточность, общие данные и мьютексы

Введение

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

Для блокировки общих данных от одновременного доступа необходимо использовать объекты синхронизации.

В данном топике рассмотренна методика работы с мютексами, существенно уменьшающая количество потенциальных ошибок связанных с созданием/удалением и захватом/освобождением.

Неудаление мютекса приводит к утечке памяти, незахват — к некорректным данным, а неосвобождение — к блокировке всех функций, работающих с общими данными.

Ниже рассматривается работа с мютексами в Windows и Unix, подобная идея может быть использована при работе с другими объектами синхронизации.

Эта идея является частным случаем методики «Выделение ресурса — есть инициализация (RAII)».

Создание, настройка и удаление мютекса

Для начала объявим класс CAutoMutex, который создает мютекс в конструкторе и удаляет в деструторе.
Плюсы:
— не нужно плодить по всему проекту похожие фрагменты коды инициализации, настройки и удаления мютекса
— автоматическое удаление мютекса и освобождение ресурсов, занятых им

// класс-оболочка, создающий и удаляющий мютекс (Windows)
class CAutoMutex
<
// дескриптор создаваемого мютекса
HANDLE m_h_mutex;

// запрет копирования
CAutoMutex( const CAutoMutex&);
CAutoMutex& operator =( const CAutoMutex&);

public :
CAutoMutex()
<
m_h_mutex = CreateMutex(NULL, FALSE, NULL);
assert(m_h_mutex);
>

В Windows мютексы по умолчанию рекурсивные, а в Unix — нет. Если мютекс не является рекурсивным, то попытка захватить его два раза в одном потоке приведет к deadlock-у.
Чтобы в Unix создать рекурсивный мютекс, необходимо установить соответствующий флаг при инициализации. Соответствующий класс CAutoMutex выглядел бы так (проверки возвращаемых значений не показаны для компактности):

// класс-оболочка, создающий и удаляющий рекурсивный мютекс (Unix)
class CAutoMutex
<
pthread_mutex_t m_mutex;

public :
CAutoMutex()
<
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_mutex, &attr);
pthread_mutexattr_destroy(&attr);
>

Захват и освобождение мютекса

По аналогии с предыдущим классом объявим класс CMutexLock, который занимает мютекс в конструкторе и освобождает в деструкторе. Созданный объект этого класса автоматически захватит мютекс и освободит его в конце области действия независимо от того, какой именно был выход из этой области: нормальный выход, преждевременный return или выброс исключения. Плюсом также является, что можно не плодить похожие фрагменты кода работы с мютексами.

// класс-оболочка, занимающий и освобождающий мютекс
class CMutexLock
<
HANDLE m_mutex;

// запрещаем копирование
CMutexLock( const CMutexLock&);
CMutexLock& operator =( const CMutexLock&);
public :
// занимаем мютекс при конструировании объекта
CMutexLock(HANDLE mutex): m_mutex(mutex)
<
const DWORD res = WaitForSingleObject(m_mutex, INFINITE);
assert(res == WAIT_OBJECT_0);
>
// освобождаем мютекс при удалении объекта

Для еще большего удобства объявим следующий макрос:

Макрос позволяет не держать в голове имя класса CMutexLock и его пространство имен, а также не ломать голову каждый раз над названием создаваемого (например _tmp_mtx_capt) объекта.

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

Рассмотрим примеры использования.

Для упрощения примера объявим мютекс и общие данные в глобальной области:

// автоматически создаваемый и удаляемый мютекс
static CAutoMutex g_mutex;

Пример простой функции, использующей общие данные и макрос SCOPE_LOCK_MUTEX:

<
// занимаем мютекс
SCOPE_LOCK_MUTEX(g_mutex. get ());

// изменяем общие данные
g_common_cnt_ex = 0;
g_common_cnt = 0;

// здесь мютекс освобождается
>

Не правда ли, что функция do_sth_1() выглядит элегантнее, чем следующая? do_sth_1_eq:

void do_sth_1_eq( ) throw ()
<
// занимаем мютекс
if (WaitForSingleObject(g_mutex. get (), INFINITE) == WAIT_OBJECT_0)
<
// изменяем общие данные
g_common_cnt_ex = 0;
g_common_cnt = 0;

// надо не забыть освободить мютекс
ReleaseMutex(g_mutex. get ());
>
else
<
assert(0);
>
>

В следующем примере точек выхода из функции три, но упоминание о мютексе только одно (объявление области блокировки мютекса):

// занимаем мютекс на критическом участке
SCOPE_LOCK_MUTEX(g_mutex. get ());

if (rem == 1)
<
g_common_cnt_ex++;
// мютекс автоматически освободится при выбросе исключения
throw Ex();
>
else if (rem == 2)
<
// мютекс автоматически освободится при возврате
g_common_cnt++;
return 1;
>

Примечание: я не сторонник использовать несколько return-ов в одной функции, просто пример от этого
становится чуть показательнее.
А если бы функция была длиннее и точек выброса исключений было бы с десяток? Без макроса нужно было поставить перед каждой из них ReleaseMutex(. ), а ошибиться здесь можно очень легко.

Заключение

Приведенные примеры классов и макросов достаточно просты, они не содержат сложных проверок и ожидают освобождения мютекса в течение бесконечного времени. Но даже это облегчает жизнь во многих случаях. А если облегчает жизнь, то почему бы это не использовать?

UPD: Первый класс CAutoMutex по ошибке не был написан, вместо него было повторное объявление второго класса CMutexLock. Исправлено.

UPD2: Убраны слова inline в объявлении методов внутри классов за ненадобностью.

UPD3: Был добавлен вариант класса CAutoMutex с рекурсивным мютексом для Unix.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *