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

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

13 ответов

Решение

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

У них есть имена shared_lock, unique_lock, а также upgrade_lock и работать на shared_mutex,

Вы можете использовать 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
}

Использование стандартных предварительно протестированных, предварительно собранных вещей всегда хорошо (например, 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, но основная идея здесь. Эта реализация слегка смещена в сторону писателей (орда писателей может голодать читателей бесконечно); просто изменить writer_unlock если вы предпочитаете баланс наоборот.

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

редактировать

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

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

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

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

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

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

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

http://www.threadingbuildingblocks.org/

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

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

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

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard а также ACE_Read_Guard
  • ACE_Condition

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

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

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

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

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

      #include <shared_mutex>

class Foo {
 public:
  void Write() {
    std::unique_lock lock{mutex_};
    // ... 
  }

  void Read() {
    std::shared_lock lock{mutex_};
    // ... 
  }

 private:
  std::shared_mutex mutex_;
};

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

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

Если ничего другого, это руководство.

Другие вопросы по тегам