Открыть сервис

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

Блокировка чтения-записи (англ. readers–writer lock, shared-exclusive lock) — это механизм синхронизации доступа к общему ресурсу в параллельных вычислениях, который позволяет нескольким потокам одновременно читать данные, но предоставляет эксклюзивный доступ только одному потоку при записи. Данный подход повышает производительность в сценариях, где операции чтения значительно преобладают над операциями записи, за счёт снижения конкуренции между потоками.

Основные принципы работы

Блокировка чтения-записи основана на разделении доступа на два режима:

  • Режим чтения (shared lock) — любое количество потоков может одновременно удерживать блокировку в этом режиме. Потоки, выполняющие чтение, не мешают друг другу, так как не изменяют данные.
  • Режим записи (exclusive lock) — только один поток может удерживать блокировку в этом режиме. Во время записи никакие другие потоки не могут ни читать, ни записывать данные.

Переключение между режимами происходит следующим образом: если ни один поток не удерживает блокировку, запрос на чтение или запись удовлетворяется немедленно. Если блокировка удерживается в режиме чтения, новый запрос на чтение также удовлетворяется, а запрос на запись ставится в очередь. Если блокировка удерживается в режиме записи, все последующие запросы (как на чтение, так и на запись) блокируются до её освобождения.

Классификация

По приоритету доступа

Существует несколько стратегий обработки конкуренции между читателями и писателями:

  • Читатели с приоритетом (readers-preference) — если блокировка удерживается читателями, новые читатели могут получить доступ, даже если в очереди ожидает писатель. Это может привести к «голоданию» писателей, когда они никогда не получат доступа из-за непрерывного потока читателей.
  • Писатели с приоритетом (writers-preference) — как только писатель заявляет о намерении записать данные, новые читатели не допускаются до тех пор, пока писатель не завершит работу. Это предотвращает голодание писателей, но может снизить параллелизм чтения.
  • Справедливая блокировка (fair lock) — запросы обрабатываются в порядке очереди (FIFO). Ни читатели, ни писатели не имеют преимущества, что обеспечивает равномерный доступ, но может снизить общую пропускную способность.

По реализации

  • Блокирующие (blocking) — поток, не получивший доступ, переводится в состояние ожидания (sleep) до освобождения ресурса. Требуют поддержки со стороны операционной системы.
  • Спин-блокировки (spinlock) — поток активно проверяет состояние блокировки в цикле, не переходя в режим ожидания. Эффективны при коротких операциях, но потребляют процессорное время.
  • Блокировки с тайм-аутом — поток ожидает доступ в течение заданного времени, после чего возвращает ошибку. Используются для предотвращения взаимных блокировок (deadlock).

Реализация

Программные интерфейсы

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

  • POSIX Threads (pthreads)pthread_rwlock_t в Linux и других Unix-подобных системах. Поддерживает операции pthread_rwlock_rdlock, pthread_rwlock_wrlock и pthread_rwlock_unlock.
  • Windows APISRWLOCK (Slim Reader/Writer Lock) в Windows Vista и более новых версиях. Используется с функциями AcquireSRWLockShared, AcquireSRWLockExclusive и ReleaseSRWLock.
  • JavaReentrantReadWriteLock из пакета java.util.concurrent.locks. Позволяет настраивать справедливость и поддерживает реентерабельность (повторный захват блокировки тем же потоком).
  • C++std::shared_mutex (начиная с C++17) и std::shared_lock для чтения, std::lock_guard для записи.
  • Pythonthreading.RLock не поддерживает разделение; для блокировок чтения-записи используются сторонние библиотеки, например readerwriterlock.

Пример реализации на C++

```cpp

include <shared_mutex>

include <thread>

include <vector>

std::shared_mutex rw_mutex; int shared_data = 0;

void reader() { std::shared_lock lock(rw_mutex); // Чтение shared_data }

void writer() { std::unique_lock lock(rw_mutex); // Запись в shared_data } ```

Применение

Блокировки чтения-записи широко используются в следующих областях:

  • Базы данных — системы управления базами данных (СУБД) применяют этот механизм для управления одновременным доступом к таблицам и строкам. Например, в PostgreSQL используется блокировка на уровне строк, где читатели не блокируют других читателей.
  • Файловые системы — операционные системы используют блокировки чтения-записи для синхронизации доступа к файлам. В Linux системный вызов flock поддерживает раздельные блокировки на чтение и запись.
  • Кэширование — в многопоточных кэшах (например, в веб-серверах) чтение данных из кэша происходит часто, а запись — редко. Блокировка чтения-записи позволяет обслуживать множество запросов параллельно.
  • Редакторы кода и текстовые процессоры — при совместной работе над документами несколько пользователей могут одновременно читать файл, но только один — вносить изменения.
  • Системы реального времени — в авионике и промышленных контроллерах, где требуется детерминированное поведение, используются блокировки с приоритетом писателей для гарантии актуальности данных.

Преимущества и недостатки

Преимущества

  • Повышенная производительность — при высокой доле операций чтения (более 80–90%) блокировка чтения-записи значительно эффективнее обычной взаимной блокировки (mutex), так как позволяет параллельное чтение.
  • Гибкость — возможность настройки приоритета позволяет адаптировать механизм под конкретные сценарии использования.
  • Предотвращение гонок данных — гарантирует, что ни один поток не прочитает данные в момент их изменения.

Недостатки

  • Сложность реализации — неправильная обработка приоритетов может привести к голоданию (starvation) или взаимным блокировкам (deadlock).
  • Накладные расходы — управление очередями и переключение контекста требуют дополнительных ресурсов по сравнению с простым mutex.
  • Неэффективность при коротких операциях — если время чтения или записи мало, накладные расходы на блокировку могут превысить выгоду от параллелизма.
  • Проблема «читательского голодания» — при приоритете читателей писатели могут никогда не получить доступ, если читатели постоянно активны.

Альтернативы

  • Взаимная блокировка (mutex) — простейший механизм, предоставляющий эксклюзивный доступ. Подходит для сценариев с равной долей чтения и записи.
  • Блокировка без ожидания (lock-free) — алгоритмы, использующие атомарные операции (CAS — compare-and-swap) для синхронизации без блокировок. Применяются в высоконагруженных системах, где блокировки недопустимы.
  • Транзакционная память (Transactional Memory) — аппаратная или программная поддержка атомарных блоков кода, упрощающая написание параллельных программ.
  • Семафоры — более общий механизм синхронизации, который может эмулировать блокировку чтения-записи, но требует ручного управления.

Интересные факты

  • Первое описание блокировки чтения-записи в научной литературе датируется 1971 годом в работе «Concurrent Control with Readers and Writers» Питера Дж. Куртона (Peter J. Courtois) и его коллег.
  • В операционной системе Linux реализация pthread_rwlock_t использует атомарные операции и спин-блокировки для коротких ожиданий, что повышает производительность на многоядерных процессорах.
  • В языке программирования Go блокировка чтения-записи реализована через sync.RWMutex, который автоматически предотвращает голодание писателей, отдавая им приоритет после определённого времени ожидания.

Источники

  • Courtois, P. J., Heymans, F., & Parnas, D. L. (1971). «Concurrent Control with Readers and Writers». Communications of the ACM.
  • Herlihy, M., & Shavit, N. (2012). «The Art of Multiprocessor Programming». Morgan Kaufmann.
  • Документация POSIX.1-2017: pthread_rwlock_rdlock, pthread_rwlock_wrlock.
  • Документация Microsoft Windows: «Slim Reader/Writer (SRW) Locks».
  • Документация Java: «ReentrantReadWriteLock» (Oracle).

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →