Daily bit(e) C++. std::shared_mutex
Daily bit(e) C++ #15, мьютекс с общими блокировками: std::shared_mutex
.
std::shared_mutex
– это вариант std::mutex
из C++14 (C++17 для несинхронизированной версии), который поддерживает два типа блокировок: эксклюзивную блокировку, которая может удерживаться только одним потоком, и общую блокировку. которая может удерживаться любым количеством потоков (пока не удерживается эксклюзивная блокировка).
Это семантика обычно необходима для доступа чтения-записи, поскольку операции чтения могут выполняться параллельно, а операции записи требуют эксклюзивного доступа.
#include <mutex>
#include <shared_mutex>
#include <array>
#include <optional>
struct Data {};
struct RecentSnapshots
{
void push(const Data& data)
{
// Мы собираемся изменить данные,
// захватываем unique_lock
std::unique_lock lock(mux_);
buffer_[offset_ % 64] = data;
++offset_;
}
std::optional<Data> get(size_t index) const
{
// Мы только читаем, но нам нужно
// предотвратить одновременную запись,
// захватываем shared_lock
std::shared_lock lock(mux_);
if (index >= offset_)
return std::nullopt;
if (offset_ >= 64 && offset_-64 > index)
return std::nullopt;
return buffer_[index % 64];
}
size_t min_offset() const
{
// Мы только читаем, но нам нужно
// предотвратить одновременную запись,
// захватываем shared_lock
std::shared_lock lock(mux_);
if (offset_ <= 64) return 0;
return offset_ - 64;
}
private:
// Нам нужен mutable, так как мы изменяем
// состояние этого мьютекса (захватом блокировки)
// в константных методах.
mutable std::shared_mutex mux_;
std::array<Data,64> buffer_;
size_t offset_ = 0;
};
// Примечание: вызов min_offset() с последующим get(offset)
// НЕ обеспечивает никакой транзакционности, поскольку
// запись может вставить себя между двумя этими вызовами.
Открыть пример на Compiler Explorer.