Может ли scoped_lock заблокировать shared_mutex в режиме чтения? - PullRequest
0 голосов
/ 12 февраля 2019

C ++ 17 представила как std::shared_mutex, так и std::scoped_lock.Теперь моя проблема в том, что scoped_lock блокирует общий мьютекс всегда в режиме монопольного доступа (записи), когда он передается в качестве аргумента, а не в режиме общего доступа (чтения).В моем приложении мне нужно обновить объект dst данными из объекта src.Я хочу заблокировать src общий и dst эксклюзивный.К сожалению, это может привести к взаимоблокировке, если в это же время происходит вызов другого метода обновления с переключенными src и dst.Поэтому я хотел бы использовать причудливые механизмы предотвращения тупиков std::scoped_lock.

. Я мог бы использовать scoped_lock для блокировки src и dst в эксклюзивном режиме, но излишне строгая блокировка имеет откат производительностив другом месте.Однако, похоже, что можно src обернуть shared_mutex в std::shared_lock и использовать это с scoped_lock: когда scoped_lock во время действия блокировки вызывает try_lock() на shared_lock, позже он на самом деле вызовет try_shared_lock() на src 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???
}

Безопасно ли использовать (совместно) защитную блокировку, подобную этой, в другой (блокирующей блокировку) защитной блокировке?

Ответы [ 2 ]

0 голосов
/ 18 мая 2019

Mutex : добавить безопасность потоков к общим ресурсам
Lock : добавить RAII (и, возможно, дополнительную функциональность) в mutex

Различные блокировки позволяют блокироватьмьютекс по-разному:

  • unique_lock: эксклюзивный доступ к ресурсу (для записи)
  • shared_lock: общий доступ к ресурсу (для одновременного чтения)
  • scoped_lock: то жеas unique_lock, но с меньшим количеством функций

scoped_lock - это эксклюзивный замок с голой костью, который блокируется при его создании и разблокируется при его разрушении.unique_lock и shared_lock - это эксклюзивные и общие блокировки соответственно, которые также блокируются и разблокируются с помощью конструктора и деструктора по умолчанию.Тем не менее, они также обеспечивают дополнительную функциональность.Например, вы можете попытаться везти их , или вы можете разблокировать их до того, как они будут уничтожены.

Так что типичным вариантом использования будет использование shared_lock для общего доступа (когданесколько потоков читают один и тот же ресурс) и используют unique_lock или scoped_lock для эксклюзивного доступа (в зависимости от того, нужны ли вам дополнительные функции unique_lock или нет).

0 голосов
/ 15 февраля 2019

Как отмечают различные комментаторы, прочитавшие код реализации стандартной библиотеки 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 в соответствииto cppreference - это оболочка для std::lock, с дополнительным функционалом вызова unlock() для каждого объекта Lockable в его деструкторе.Эта дополнительная функциональность здесь не требуется, так как std::shared_lock lk1 и std::unique_lock lk2 также работают в качестве защитных устройств блокировки, которые разблокируют свои мьютексы, когда они выходят из области видимости.

Редактировать: различные уточнения

...