Блокировка чтения/записи в C++


Я ищу хорошую блокировку чтения / записи в C++. У нас есть случай использования одного нечастого писателя и многих частых читателей, и мы хотели бы оптимизировать для этого. Предпочтительнее я хотел бы кросс-платформенное решение, однако Windows только один будет приемлемым.

12 59

12 ответов:

Более новые версии boost::thread имеют блокировки чтения/записи (1.35.0 и более поздние версии, по-видимому, предыдущие версии работали неправильно).

У них есть имена shared_lock, unique_lock, и еще upgrade_lock и оперировать на а shared_mutex.

Использование стандартных предварительно протестированных, предварительно построенных материалов всегда хорошо (например, Boost, как предложил другой ответ), но это то, что не слишком сложно построить самостоятельно. Вот глупая маленькая реализация, вытащенная из моего проекта:

#include <pthread.h>

struct rwlock {
    pthread_mutex_t lock;
    pthread_cond_t read, write;
    unsigned readers, writers, read_waiters, write_waiters;
};

void reader_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->writers || self->write_waiters) {
        self->read_waiters++;
        do pthread_cond_wait(&self->read, &self->lock);
        while (self->writers || self->write_waiters);
        self->read_waiters--;
    }
    self->readers++;
    pthread_mutex_unlock(&self->lock);
}

void reader_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->readers--;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    pthread_mutex_unlock(&self->lock);
}

void writer_lock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    if (self->readers || self->writers) {
        self->write_waiters++;
        do pthread_cond_wait(&self->write, &self->lock);
        while (self->readers || self->writers);
        self->write_waiters--;
    }
    self->writers = 1;
    pthread_mutex_unlock(&self->lock);
}

void writer_unlock(struct rwlock *self) {
    pthread_mutex_lock(&self->lock);
    self->writers = 0;
    if (self->write_waiters)
        pthread_cond_signal(&self->write);
    else if (self->read_waiters)
        pthread_cond_broadcast(&self->read);
    pthread_mutex_unlock(&self->lock);
}

void rwlock_init(struct rwlock *self) {
    self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
    pthread_mutex_init(&self->lock, NULL);
    pthread_cond_init(&self->read, NULL);
    pthread_cond_init(&self->write, NULL);
}

pthreads на самом деле это не Windows-native, но общая идея здесь. Эта реализация слегка смещена в сторону писателей (Орда писателей может голодать читателей бесконечно); просто измените writer_unlock, Если вы хотите, чтобы баланс был все наоборот.

Да, это C, а не C++. Перевод-это упражнение, оставленное читателю.

Править

Грег Роджерс отметил, что стандарт POSIX действительно определяет pthread_rwlock_*. Это не поможет, если у вас нет pthreads, но это заставило мой ум вспомнить: Pthreads-w32 должен работать! Вместо того чтобы переносить этот код в не - pthreads для собственного использования, просто используйте Pthreads-w32 в Windows и native pthreads везде.

Вы можете использовать boost для создания блокировки чтения-записи:

#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;

Lock myLock;


void ReadFunction()
{
    ReadLock r_lock(myLock);
    //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

Что бы вы ни решили использовать, сравните свою рабочую нагрузку с простыми блокировками, поскольку блокировки чтения/записи, как правило, в 3-40 раз медленнее, чем простой мьютекс, когда нет разногласий.

Вот некоторая Ссылка

Edit: ссылка на журнал MSDN больше не доступна. Статья CodeProject теперь доступна на https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks и подытоживает это довольно красиво. Также я нашел новую ссылку MSDN осоставных объектах синхронизации .

Есть статья о блокировках reader-writer на MSDN, в которой представлены некоторые их реализации. Он также вводит Slim reader / writer lock, примитив синхронизации ядра познакомился с Vista. Существует также статьяCodeProject о сравнении различных реализаций (включая статьи MSDN).

Intel Thread Building Blocks также предоставляют несколько вариантов rw_lock:

Http://www.threadingbuildingblocks.org/

У них есть spin_rw_mutex для очень коротких периодов конкуренции и queueing_rw_mutex для более длительных периодов конкуренции. Первый может быть использован в особо чувствительном к производительности коде. Последний более сопоставим по производительности с тем, что обеспечивает Boost.Поток или непосредственно с помощью pthreads. Но профиль, чтобы убедиться, какой из них является выигрышем для вашего модель доступа.

Толчок.Thread с момента выпуска 1.35.0 уже поддерживает блокировки reader-writer. Хорошая вещь в этом заключается в том, что реализация является в значительной степени кросс-платформенной, рецензируемой и фактически эталонной реализацией для предстоящего стандарта C++0x.

Я могу порекомендовать библиотеку ACE , которая предоставляет множество механизмов блокировки и портируется на различные платформы.

В зависимости от граничных условий вашей задачи, вы можете найти следующие классы полезными:

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard и ACE_Read_Guard
  • ACE_Condition

Http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx

Вот хорошая и легкая реализация, подходящая для большинства задач.

Класс блокировки синхронизации с несколькими читателями и одним писателем для Win32 от Glenn Slayde

Http://www.glennslayden.com/code/win32/reader-writer-lock

Поддержка C++17std::shared_mutex . Он поддерживается в MSVC++ 2015 и 2017.

Вы могли бы скопировать отличный Sun ReentrantReadWriteLock . Он включает в себя такие функции, как необязательная справедливость, понижение уровня блокировки и, конечно же, повторный вход.

Да, это на Java, но вы можете легко прочитать и перенести его на C++, даже если вы не знаете никакой Java. Документация, на которую я ссылался, содержит все поведенческие свойства этой реализации, поэтому вы можете убедиться, что она делает то, что вы хотите.

Во всяком случае, это путеводитель.