Может ли scoped_lock заблокировать shared_mutex в режиме чтения?
C++17 представил оба std::shared_mutex
а также std::scoped_lock
, Моя проблема сейчас в том, что кажется, что scoped_lock
блокирует общий мьютекс всегда в монопольном (записывающем) режиме, когда он передается в качестве аргумента, а не в общедоступном (читательском) режиме. В моем приложении мне нужно обновить объект dst
с данными из объекта src
, Я хочу заблокировать src
поделился и dst
эксклюзив. К сожалению, это может привести к тупику, если вызов другого метода обновления с src
а также dst
переключение происходит одновременно. Поэтому я хотел бы использовать причудливые механизмы std::scoped_lock
,
Я мог бы использовать scoped_lock
заблокировать оба src
а также dst
в эксклюзивном режиме, но эта неоправданно строгая блокировка имеет откат производительности в других местах. Однако, кажется, что это можно завернуть src
"s shared_mutex
в std::shared_lock
и использовать это с scoped_lock
: Когда scoped_lock
во время его вызова блокировки действий try_lock()
на shared_lock
Позже позвоню try_shared_lock()
на src
"s shared_mutex
и вот что мне нужно.
Так что мой код выглядит так просто:
struct data {
mutable std::shared_mutex mutex;
// actual data follows
};
void update(const data& src, data& dst)
{
std::shared_lock slock(src.mutex, std::defer_lock);
std::scoped_lock lockall(slock, dst.mutex);
// now can safely update dst with src???
}
Безопасно ли использовать (совместно используемый) защитный кожух, как этот, внутри другого защитного кожуха?
1 ответ
Как отмечают различные комментаторы, которые читали код реализации стандартной библиотеки C++: да, использование std::shared_mutex
завернутый в std::shared_lock()
в качестве одного из аргументов std::scoped_lock()
безопасно.
В основном, std::shared_lock
переадресация всех звонков на lock()
в lock_shared()
на мьютекс.
std::shared_lock::lock -----------> mutex()->lock_shared(). // same for try_lock etc..
Другое возможное решение
std::shared_lock lk1(src.mutex, std::defer_lock);
std::unique_lock lk2(dst.mutex, std::defer_lock);
std::lock(lk1, lk2);
std::lock
это функция, которая принимает любое количество Lockable
все объекты и блокирует их (или отменяет за исключением, в этом случае все они будут разблокированы).
std::scoped_lock
согласно cppreference является оберткой для std::lock
с добавленным функционалом звонка unlock()
на каждом запираемом объекте в его деструкторе. Эта дополнительная функциональность здесь не требуется, так как std::shared_lock lk1
а также std::unique_lock lk2
также работают в качестве охранников блокировки, которые открывают свои мьютексы, когда они выходят за рамки видимости.
Изменить: различные уточнения
Mutex: добавьте безопасность потоков в общие ресурсы
Блокировка: добавьте RAII (и, возможно, дополнительную функциональность) в мьютекс
Различные блокировки позволяют блокировать мьютекс разными способами:
- unique_lock: эксклюзивный доступ к ресурсу (для записи)
- shared_lock: общий доступ к ресурсу (для одновременного чтения)
- scoped_lock: такой же как unique_lock, но с меньшим количеством функций
scoped_lock
является эксклюзивным замком с голыми костями, который блокируется при его создании и разблокируется при его разрушении. unique_lock
а также shared_lock
Это эксклюзивные и разделяемые блокировки соответственно, которые также блокируются и разблокируются с помощью конструктора и деструктора по умолчанию. Тем не менее, они также обеспечивают дополнительную функциональность. Например, вы можете попытаться везти их, или вы можете разблокировать их, прежде чем они будут уничтожены.
Таким образом, типичный вариант использования будет использовать shared_lock
для общего доступа (когда несколько потоков читают один и тот же ресурс) и используют unique_lock
или scoped_lock
для эксклюзивного доступа (в зависимости от того, нужны ли вам дополнительные функции unique_lock
или нет).