boost::shared_mutex
или std::shared_mutex
(C++ 17) может использоваться для одиночной записи, доступа нескольких считывателей. В качестве учебного упражнения я собрал простую реализацию, которая использует спин-блокировку и имеет другие ограничения (например, политику справедливости), но явно не предназначена для использования в реальных приложениях.Реализация C++ shared_mutex
Идея состоит в том, что мьютекс сохраняет счетчик ссылок, равный нулю, если нить не удерживает блокировку. Если> 0, значение представляет количество читателей, имеющих доступ. Если -1, у одного писателя есть доступ.
Является ли это правильной реализацией (в частности, с использованием минимальных порядков памяти), которая не содержит данных?
#include <atomic>
class my_shared_mutex {
std::atomic<int> refcount{0};
public:
void lock() // write lock
{
int val;
do {
val = 0; // Can only take a write lock when refcount == 0
} while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire));
// can memory_order_relaxed be used if only a single thread takes write locks ?
}
void unlock() // write unlock
{
refcount.store(0, std::memory_order_release);
}
void lock_shared() // read lock
{
int val;
do {
do {
val = refcount.load(std::memory_order_relaxed);
} while (val == -1); // spinning until the write lock is released
} while (!refcount.compare_exchange_weak(val, val+1, std::memory_order_acquire));
}
void unlock_shared() // read unlock
{
refcount.fetch_sub(1, std::memory_order_relaxed);
}
};
Я не был уверен в порядках памяти в unlock_shared, но мои рассуждения состояли в том, что он действительно не «выпускает» что-либо, поскольку имеет доступ только для чтения и не может изменить данные, которые он защищает. – LWimsey
@LWimsey: да, это тяжелее думать о заказе груза, чем заказать магазин, но это реальная вещь. Нагрузка становится глобально видимой в тот момент, когда она считывает данные из кеша L1. (Потому что это то, что делает копию из глобально-когерентного кеша в ядро из-за порядка одного процессора.) –