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

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

Блокировки чтения/записи применяются точно в соответствии с их названием: несколько «читателей» могут использовать ресурс в отсутствие «писателей», или один «писатель» может использовать ресурс в отсутствие «читателей» и других «писателей».

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

У вас будет часто возникать ситуация разделения структуры данных группой потоков. Очевидно, что в любой момент времени только один поток может записывать данные в эту структуру. Если бы запись велась более чем одним потоком одновременно, одни потоки могли бы записать свои данные поверх данных других потоков. Для предотвращения таких ситуаций поток-«писатель» должен эксклюзивно получить блокировку чтения/записи («rwlock»), обозначив этим, что он и только он имеет доступ к структуре данных. Заметьте, что это исключительное право доступа «строго контролируется на добровольных началах» — обеспечение того, чтобы все потоки, которые пользуются указанной областью данных, синхронизировались с использованием блокировок чтения/ записи, зависит только от вас.

С «читателями» ситуация противоположная. Поскольку считывание области данных — неразрушающая операция, любое число потоков может считывать данные (даже если ту же часть данных в этот момент считывает другой поток). Сложным моментом здесь является то, что никто не должен производить запись в область данных, из которой в этот момент ведется чтение. В противном случае, считывающие потоки могут быть «введены в заблуждение» — например, поток мог бы считать часть данных, затем быть вытесненным потоком-«писателем» затем возобновиться и продолжить считывание данных, но уже обновленных! Это может закончиться нарушением целостности данных.

Давайте рассмотрим вызовы, которые вы могли бы использовать при применении блокировок чтения/записи.

Первые два вызова используются для инициализации внутренних областей памяти для rwlock-блокировок (чтения/записи):

int pthread_rwlock_init(pthread_rwlock_t *lock,

 const pthread_rwlockattr_t *attr);

int pthread_rwlock_destroy(pthread_rwlock_t *lock);

Функция pthread_rwlock_init() принимает аргумент lock (типа pthread_rwlock_t) и инициализирует его атрибутами, указанными в параметре attr. В нашем примере мы применим атрибут NULL, что будет означать «применить значения по умолчанию». Более подробно об этом см. документацию на функции:

pthread_rwlockattr_init();

pthread_rwlockattr_destroy();

pthread_rwlockattr_getpshared();

pthread_rwlockattr_setpshared().

Когда мы закончим свои дела с блокировкой чтения/записи, её следует уничтожить функцией pthread_rwlock_destroy().

Никогда не используйте блокировку, которая либо уже уничтожена, либо еще не инициализирована.

Далее, мы должны выбрать блокировку подходящего типа. Как отмечалось выше, в основном применяются два режима блокировки: «читателю» желательно иметь «неэксклюзивный» доступ, а для «писателю» — «эксклюзивный». Для упрощения имен, функции названы по именам своих пользователей:

int pthread_rwlock_rdlock(pthread_rwlock_t *lock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock);

int pthread_rwlock_wrlock(pthread_rwlock_t* lock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *lock);

Существует четыре функции блокировки, а не две, как вы могли бы предположить. Очевидно, «предполагаемыми» функциями были pthread_rwlock_rdlock() и pthread_rwlock_wrlock(), используемые «читателями» и «писателями», соответственно.

Это — собственно блокирующие вызовы: если блокировка для выбранной операции недоступна, поток будет блокирован. Когда блокировка становится доступной в соответствующем режиме, поток будет разблокирован, из чего он сможет предположить, что теперь можно спокойно обращаться к защищенному блокировкой ресурсу.

Иногда, тем не менее, поток может не захотеть блокироваться, желая вместо этого просто узнать, доступна ли нужная блокировка. Для этого и существуют версии функций, содержащие в имени «try» («проверка»). Важно отметить, что «проверочные» версии получат блокировку, если она доступна, но если нет, тогда они не будут блокированы, а только возвратят код ошибки. Причина, по которой они должны получать блокировку, если она доступна, очень проста. Предположим, что поток хочет получить блокировку на чтение, но не хочет ждать, если блокировка окажется недоступной. Поток вызывает функцию pthread_rwlock_tryrdlock(), и оказывается, что блокировка доступна. Если бы функция pthread_rwlock_tryrdlock() не захватывала доступную блокировку немедленно, могли бы произойти неприятные вещи — наш поток мог бы быть, к примеру, вытеснен другим потоком, а тот, в свою очередь, мог бы блокировать нужный нам ресурс. Поскольку первому потоку фактически не была предоставлена блокировка, после возобновления ему придется вызывать pthread_rwlock_rdlock(), и вот теперь он будет заблокирован, поскольку ресурс более недоступен. Иными словами, в такой ситуации даже поток, не желающий блокироваться и поэтому вызывающий «проверочную» версию, по-прежнему может быть заблокирован!

Наконец, независимо от того, как блокировка нами применялась, нам необходим способ ее освобождения:

int pthread_rwlock_unlock(pthread_rwlock_t* lock);

После того как поток выполнил нужную операцию с ресурсом, он освобождает блокировку, вызывая функцию pthread_rwlock_unlock(). Если блокировка теперь становится доступной в режиме, который запрошен и ожидается другим потоком, то этот ждущий поток будет переведен в состояние готовности (READY).

Отметим, что мы не смогли бы реализовать такую форму синхронизации только с помощью мутекса. Мутекс рассчитан только на один поток, что было бы хорошо в случае записи (чтобы только один поток мог использовать ресурс в определенный момент времени), но оплошал бы в случае считывания, потому что не допустил бы к ресурсу более чем одного «читателя». Семафор также был бы бесполезен, потому что нельзя было бы отличить два режима доступа — применение семафора могло бы обеспечить доступ нескольких «читателей», но если бы семафором попытался завладеть «писатель», его вызов ничем бы не отличался от вызова «читателей», что вызвало бы некрасивую ситуацию с множеством «читателей» и множеством же «писателей»!

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

1.4. Устройства чтения/записи CD и DVD

Из книги Nero 8 автора Кашеваров А

1.4. Устройства чтения/записи CD и DVD В настоящее время существуют устройства, которые могут только читать CD и DVD, но не могут их записывать, и устройства, которые могут и читать, и записывать диски. Первый тип устройств называется приводами CD-ROM или DVD-ROM. Они сравнительно


Основные принципы чтения и записи XML-данных

Из книги Обработка баз данных на Visual Basic®.NET автора Мак-Манус Джеффри П

Основные принципы чтения и записи XML-данных В главах 5, "ADO.NET: объект DataSet", и 6, "ADO.NET: объект DataAdapter" демонстрируются программируемые и прямые способы загрузки данных в объект DataSet из базы данных. Еще один метод загрузки данных основан на чтении XML-данных. Как и следовало


2.3.3.1 Прозрачный доступ для чтения и записи

Из книги Руководство администратора баз данных Informix. автора Кустов Виктор

2.3.3.1 Прозрачный доступ для чтения и записи Enterprise Gateway представляет собой единый шлюз, который обеспечивает прозрачный доступ к данным в масштабах предприятия. Конечные пользователи обращаются к Enterprise Gateway так же, как к серверу баз данных INFORMIX. Доступ на чтение и запись


ГЛАВА 8 Блокировки чтения-записи

Из книги UNIX: взаимодействие процессов автора Стивенс Уильям Ричард

ГЛАВА 8 Блокировки чтения-записи 8.1. Введение Взаимное исключение используется для предотвращения одновременного доступа нескольких потоков к критической области. Критическая область кода обычно содержит операции считывания или изменения данных, используемых


8.2. Получение и сброс блокировки чтения-записи

Из книги Системное программирование в среде Windows автора Харт Джонсон М

8.2. Получение и сброс блокировки чтения-записи Блокировка чтения-записи имеет тип pthread_rwlock_t. Если переменная этого типа является статической, она может быть проинициализирована присваиванием значения константы PTHREAD_RWLOCK_INITIALIZER.Функция pthread_rwlock_rdlock позволяет заблокировать


8.3. Атрибуты блокировки чтения-записи

Из книги QNX/UNIX [Анатомия параллелизма] автора Цилюрик Олег Иванович

8.3. Атрибуты блокировки чтения-записи Мы уже отмечали, что статическая блокировка может быть проинициализирована присваиванием ей значения PTHREAD_RWLOCK_INITIALIZER. Эти переменные могут быть проинициализированы и динамически путем вызова функции pthread_rwlock_init. Когда поток


9.6. Приоритет чтения и записи

Из книги IT-безопасность: стоит ли рисковать корпорацией? автора Маккарти Линда

9.6. Приоритет чтения и записи В нашей реализации блокировок чтения-записи в разделе 8.4 приоритет предоставлялся ожидающим записи процессам. Теперь мы изучим детали возможного решения задачи читателей и писателей с помощью блокировки записей fcntl. Хочется узнать, как


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

Из книги Разработка ядра Linux автора Лав Роберт

Блокировки чтения-записи Пpoгрaммa, использующая блокировки чтения-записи, является слегка измененной версией программы с взаимными исключениями Posix. Поток должен установить блокировку файла, прежде чем увеличивать общий счетчик. ПРИМЕЧАНИЕ Существует не так уж много


Операции открытия, чтения, записи и закрытияфайлов

Из книги автора

Операции открытия, чтения, записи и закрытияфайлов Первой функцией Windows, которую мы подробно опишем, является функция CreateFile, используемая как для создания новых, так и для открытия существующих файлов. Для этой функции, как и для всех остальных, сначала приводится


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

Из книги автора

Блокировки чтения/записи Блокировка чтения/записи является специфическим механизмом, отличающимся от рассмотренных выше. Специфика состоит в следующем:• Данный тип блокировки даже по POSIX является альтернативным. Часто этот тип блокировки может реализовываться как


Операции с блокировками чтения/записи

Из книги автора

Операции с блокировками чтения/записи Инициализация объекта блокировки int pthread_rwlock_init(pthread_rwlock_t* rwl, const pthread_rwlockattr_t* attr);int pthread_rwlock_destroy(pthread_rwlock_t* rwl);Вызов функций инициализирует/разрушает блокировку чтения/записи. При инициализации блокировки ей передается структура


Захват блокировки чтения/записи

Из книги автора

Захват блокировки чтения/записи В связи со спецификой применения блокировок чтения/записи этот объект синхронизации имеет две группы функций захвата, позволяющих по-разному регулировать доступ к защищаемому участку кода.К первой группе относятся функции, допускающие


Использование блокировок чтения/записи

Из книги автора

Использование блокировок чтения/записи Построим приложение, использующее блокировку чтения/записи (файл sy10.cc):Эффективность блокировки чтения/записи#include <sys/syspage.h>#include <sys/neutrino.h>#include <list>// сколь угодно сложные элементы внутренней базы данных// приложения; в


Не экспортировать глобальные разрешения чтения/записи

Из книги автора

Не экспортировать глобальные разрешения чтения/записи Не делайте этого! Разрешения на доступ к файлам, устанавливающие, кто может читать и изменять файл, — очень простое понятие. Главное заключается в том, что чем больше вы предоставляете доступа к файлам вашей системы,


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

Из книги автора

Спин-блокировки чтения-записи Иногда в соответствии с целью использования блокировок их можно разделить два типа — блокировки чтения (reader lock) и блокировки записи (writer lock). Рассмотрим некоторый список, который может обновляться и в котором может выполняться поиск. Когда


Семафоры чтения-записи

Из книги автора

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