В этом примере мы напишем программу, которая использует экземпляр класса std::shared_mutex
lock
и unlock
самостоятельно, а сделаем это с помощью вспомогательных функций RAII.1. Сначала включим все необходимые заголовочные файлы. Поскольку мы задействуем функции и структуры данных STL, а также временные литералы, объявим об использовании пространств имен std
chrono_literal
:#include
#include
#include
#include
using namespace std;
using namespace chrono_literals;
2. Вся программа строится вокруг одного общего мьютекса, поэтому для простоты объявим его глобальный экземпляр:
shared_mutex shared_mut;
3. Мы будем использовать вспомогательные функции RAII std::shared_lock
std::unique_lock
. Чтобы их имена выглядели более понятными, определим для них короткие псевдонимы:using shrd_lck = shared_lock
using uniq_lck = unique_lock
4. Прежде чем начнем писать функцию main, определим две вспомогательные функции, которые пытаются заблокировать мьютекс в
unique_lock
для общего мьютекса. Второй аргумент конструктора defer_lock
указывает объекту поддерживать блокировку снятой. В противном случае его конструктор попробует заблокировать мьютекс, а затем будет удерживать его до завершения. Далее вызываем метод try_lock
для объекта exclusive_lock
. Этот вызов немедленно вернет булево значение, которое говорит, получили мы блокировку или же мьютекс уже был заблокирован кем-то еще.static void print_exclusive()
{
uniq_lck l {shared_mut, defer_lock};
if (l.try_lock()) {
cout << "Got exclusive lock.\n";
} else {
cout << "Unable to lock exclusively.\n";
}
}
5. Другая вспомогательная функция также пытается заблокировать мьютекс в эксклюзивном режиме. Она делает это до тех пор, пока не получит блокировку. Затем мы симулируем какую-нибудь ошибку, генерируя исключение (содержащее лишь простое целое число). Несмотря на то, что это приводит к мгновенному выходу контекста, в котором мы хранили заблокированный мьютекс, последний будет освобожден. Это происходит потому, что деструктор объекта unique_lock
static void exclusive_throw()
{
uniq_lck l {shared_mut};
throw 123;
}
6. Теперь перейдем к функции main
shared_lock
. Его конструктор мгновенно заблокирует мьютекс в коллективном режиме. Мы увидим, что это значит, в следующих шагах.int main()
{
{
shrd_lck sl1 {shared_mut};
cout << "shared lock once.\n";
7. Откроем еще одну область видимости и создадим второй экземпляр типа shared_lock
shared_lock
, и оба содержат общую блокировку мьютекса. Фактически можно создать произвольно большое количество экземпляров типа shared_lock
для одного мьютекса. Затем вызываем функцию print_exclusive
, которая пытается заблокировать мьютекс в {
shrd_lck sl2 {shared_mut};
cout << "shared lock twice.\n";
print_exclusive();
}
8. После выхода из самой поздней области видимости деструктор объекта sl2
shared_lock
освобождает свою общую блокировку мьютекса. Функция print_exclusive
снова даст сбой, поскольку мьютекс все еще находится в коллективном режиме блокировки. cout << "shared lock once again.\n";
print_exclusive();
}
cout << "lock is free.\n";