Атрибуты блокировки чтения

Download Report

Transcript Атрибуты блокировки чтения

Slide 1

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








Введение
Получение и сброс блокировки
чтения-записи
Атрибуты блокировки чтения-записи
Реализация
Отмена выполнения потоков
Пример


Slide 2

Введение




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


Slide 3

Введение

1.

2.



Правила действуют следующие:
Любое количество потоков могут заблокировать
ресурс для считывания, если ни один процесс
не заблокировал его на запись.
Блокировка
чтения-записи
может
быть
установлена на запись, только если ни один
поток не заблокировал ресурс для чтения или
для записи.
Другими словами, произвольное количество
потоков могут считывать данные, если ни один
поток не изменяет их в данный момент. Данные
могут быть изменены, только если никто другой
их не считывает и не изменяет.


Slide 4

Введение


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


Slide 5

Введение


Такой вид совместного доступа к
ресурсу
также
носит
название
совместно-исключающей
блокировки
(shared-exclusive),
поскольку
тип
используемой блокировки на чтение
называется совместной блокировкой
(shared lock), а тип используемой
блокировки на запись называется
исключающей блокировкой (exclusive
lock).


Slide 6

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




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


Slide 7

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





Функция
pthread_rwlock_wrlock
позволяет заблокировать ресурс для записи,
причем
вызвавший
процесс
будет
заблокирован, если блокировка чтениязаписи уже установлена каким-либо другим
процессом
(считывающим
или
записывающим).
Функция pthread_rwlock_unlock снимает
блокировку любого типа (чтения или записи).
Все функции возвращают 0 в случае
успешного
завершения,
положительное
значение Еххх - в случае ошибки.


Slide 8

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







#include
int
pthread_rwlock_rdlock
(pthread_rwlock_t *rwptr);
int
pthread_rwlock_wrlock
(pthread_t rwlock_t *rwptr);
int
pthread_rwlock_unlock
(
pthread_rwlock_t *rwptr);


Slide 9

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






Следующие две функции производят попытку
заблокировать ресурс для чтения или записи,
но если это невозможно, возвращают ошибку
с кодом EBUSY, вместо того чтобы
приостановить
выполнение
вызвавшего
процесса:
#include
int
pthread_rwlock_tryrdlock
(pthread_rwlock_t *rwptr);
int
pthread_rwlock_trywrlock
(pthread_rwlock_t *rwptr);


Slide 10

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


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



Когда поток перестает нуждаться в блокировке,
он может вызвать pthread_rwlock_destroy:


Slide 11

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






#include
int
pthread_rwlock_init
(pthread_rwlock_t
*rwptr,
const
pthread_rw1ockattr_t *attr);
int
pthread_rwlock_destroy
(pthread_rwlock_t *rwptr);
Обе функции возвращают 0 в случае
успешного завершения
положительное
значение Еххх - в случае ошибки.


Slide 12

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






Если при инициализации блокировки чтениязаписи attr представляет собой нулевой
указатель, атрибутам присваиваются значения
по умолчанию. Для присваивания атрибутам
других значений следует воспользоваться
двумя функциями:
#include
int
pthread_rwlockattr_init
(pthread_rwlockattr_t *attr);
int
pthread_rwlockattr_destroy
(pthread_rw1ockattr_t *attr);


Slide 13

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


После
инициализации
объекта
типа
pthread_rw1ockattr_t для установки или
сброса отдельных атрибутов используются
специальные
функции.
Единственный
определенный на настоящее время атрибут

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


Slide 14

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






#include
int pthread_rwlockattr_getpshared (const
pthread_rwlockattr_t
*attr,
int
*valptr);
int
pthread_rwlockattr_setpshared
(pthread_rwlockattr_t *attr, int value);
Первая функция возвращает текущее значение в
целом, на которое указывает аргумент valptr.
Вторая функция устанавливает значение этого
атрибута равным value, которое может быть либо
PTHREAD_PROCESS_PRIVATE,
либо
PTHREAD_PROCESS_SHARED.


Slide 15

Реализация


В
листинге
1
приведен
текст
заголовочного
файла
pthread_rwlock.h,
в
котором
определен
основной
тип
pthread_rwlock_t
и
прототипы
функций, работающих с блокировками
чтения и записи Обычно все это
находится в заголовочном файле



Slide 16

Реализация


3-13 Наш тип pthread_rwlock_t содержит
одно взаимное исключение, две условные
переменные, один флаг и три счетчика. При
просмотре или изменении содержимого этой
структуры
мы
должны
устанавливать
блокировку
rw_mutex.
После
успешной
инициализации структуры полю rw_magic
присваивается значение RW_MAGIC. Значение
этого поля проверяется всеми функциями —
таким образом гарантируется, что вызвавший
поток
передал
указатель
на
проинициализированную
блокировку.
Оно
устанавливается в 0 после уничтожения


Slide 17

Реализация




Обратите
внимание,
что
в
счетчике
rw_refcount всегда хранится текущий
статус
блокировки
чтения-записи:
-1
обозначает блокировку записи (и только одна
такая блокировка может существовать в
любой момент времени), 0 обозначает, что
блокировка
доступна
и
может
быть
установлена,
а
любое
положительное
значение
соответствует
количеству
установленных блокировок на чтение.
14-17 Мы также определяем константу для
статической
инициализации
нашей


Slide 18

Реализация






Первая функция, pthread_rwlock_init,
динамически инициализирует блокировку
чтения-записи. Ее текст приведен в листинге
2.
7-8 Присваивание атрибутов с помощью этой
функции не поддерживается, поэтому мы
проверяем, чтобы указатель attr был
нулевым.
9-19
Мы
инициализируем
взаимное
исключение и две условные переменные,
которые содержатся в нашей структуре. Все
три счетчика устанавливаются в 0, а полю


Slide 19

Реализация






20-25 Если при инициализации взаимного
исключения или условной переменной возникает
ошибка, мы уничтожаем проинициализированные
объекты и возвращаем код ошибки.
В
листинге
3
приведена
функция
pthread_rwlock_destroy,
уничтожающая
блокировку чтения-записи после окончания
работы с ней.
8-13 Прежде всего проверяется, не используется
ли блокировка в данный момент, а затем
вызываются соответствующие функции для
уничтожения взаимного исключения и двух
условных переменных.


Slide 20

Реализация





Функция pthread_rwlock_rdlock выполняет
получение блокировки на чтение.
9-10
При
работе
со
структурой
pthread_rwlock_t всегда устанавливается
блокировка на rw_mutex, являющееся ее
полем.
11-18 Нельзя получить блокировку на чтение,
если rw_refcount имеет отрицательное
значение (блокировка установлена на запись)
или имеются потоки, ожидающие получения
блокировки на запись (rw_nwaitwriters
больше 0).


Slide 21

Реализация




При разблокировании ресурса прежде всего
проверяется наличие процессов, ожидающих
возможности установить блокировку на
запись, и если таковых не существует,
проверяется
наличие
ожидающих
возможности считывания. Если они имеются,
для условной переменной rw_condreaders
передается широковещательный сигнал.
19-20 При получении блокировки на чтение
мы увеличиваем значение rw_refcount.
Блокировка взаимного исключения после
этого снимается.


Slide 22

Реализация




В
листинге
5
показана
функция
pthread_rwlock_tryrdlock, которая не
вызывает приостановления вызвавшего ее
потока.
11-14 Если блокировка в данный момент
установлена на запись или есть процессы,
ожидающие возможности установить ее на
запись, возвращается ошибка с кодом EBUSY.
В противном случае мы устанавливаем
блокировку, увеличивая значение счетчика
rw_refcount.


Slide 23

Реализация





Текст функции pthread_rwlock_wrlock приведен
в листинге 6.
11-17 Если ресурс заблокирован на считывание или
запись (значение rw_refcount отлично от 0), мы
приостанавливаем выполнение потока. Для этого
мы увеличиваем rw_nwaitwriters и вызываем
pthread_cond_wait с условной переменной
rw_condwriters.
Для
этой
переменной
посылается сигнал при снятии блокировки чтениязаписи, если имеются ожидающие разрешения на
запись процессы.
18-19 После получения блокировки на запись
устанавливаем значение rw_refcount в -1.


Slide 24

Реализация




Неблокирующая
функция
pthread_rwlock_trywrlock показана в
листинге 7.
11-14 Если значение счетчика rw_refcount
отлично от нуля, блокировка в данный
момент уже установлена считывающим или
записывающим процессом (это безразлично)
и мы возвращаем ошибку с кодом EBUSY. В
противном
случае
мы
устанавливаем
блокировку на запись, присвоив переменной
rw_refcount значение -1.


Slide 25

Реализация





Последняя функция, pthread_rwlock_unlock,
приведена в листинге 8.
16 Если rw_refcount больше 0, считывающий
поток снимает блокировку на чтение. Если
rw_refcount равно -1, записывающий поток
снимает блокировку на запись.
22 Если имеются ожидающие разрешения на
запись потоки, по условной переменной
rw_condwnters передается сигнал (если
блокировка свободна, то есть значение
счетчика rw_refcount равно 0).


Slide 26

Реализация


Мы знаем, что только один поток может
осуществлять запись, поэтому используем
функцию pthread_cond_signal. Если нет
потоков, ожидающих возможности записи, но
есть
потоки,
ожидающие
возможности
чтения, мы вызываем pthread_cond_broadcast
для переменной rw_condreaders, поскольку
возможно
одновременное
считывание
несколькими потоками.


Slide 27

Отмена выполнения потоков




Отмена выполнения может быть использована в
том случае, если несколько потоков начинают
работу над какой-то задачей (например, поиск
записи в базе данных) и один из них завершает
работу раньше всех остальных. Тогда он может
отменить их выполнение. Другим примером
является обнаружение ошибки одним из
одновременно выполняющих задачу потоков,
который затем может отменить выполнение и
всех остальных.
Для обработки отмены выполнения поток может
установить (push) или снять (pop) обработчикочиститель (cleanup handler):


Slide 28

Отмена выполнения потоков





#include
void
pthread_cleanup_push(void
(*function)(void *), void *arg);
void
pthread_cleanup_pop(int
execute);
Эти обработчики вызываются:




в случае отмены выполнения потока (другим
потоком, вызвавшим pthread_cancel);
в случае добровольного завершения работы
(вызовом pthread_exit или выходом из начальной
функции потока).


Slide 29

Отмена выполнения потоков




Обработчики-очистители выполняют всю
необходимую работу по восстановлению
значений
переменных,
такую
как
разблокирование взаимных исключений и
семафоров,
которые
могли
быть
заблокированы данным потоком.
Аргумент function представляет собой адрес
вызываемой функции, a arg — ее
единственный
аргумент.
Функция
pthread_cleanup_pop
всегда
удаляет
обработчик из верхушки стека и вызывает эту
функцию, если значение execute отлично от


Slide 30

Пример


Slide 31

Пример






На рисунке изображена временная диаграмма
выполнения нашей программы, а текст самой
программы приведен в листинге 9.
3 Создаются два потока, первый из которых
выполняет функцию threadl, а второй — thread2.
После создания первого делается пауза
длительностью в одну секунду, чтобы он успел
заблокировать ресурс на чтение.
14-23 Мы ожидаем завершения работы второго
потока и проверяем, что его статус. Затем мы
ждем завершения работы первого потока и
проверяем его статус. Затем мы уничтожаем
блокировку.


Slide 32

Пример


Поток 1 получает блокировку на чтение и
ждет 3 секунды. Эта пауза дает возможность
другому потоку вызвать pthread_rwlock_wrlock
и
заблокироваться
при
вызове
pthread_cond_wait, поскольку блокировка на
запись не может быть установлена из-за
наличия блокировки на чтение. Затем первый
поток вызывает pthread_cancel для отмены
выполнения второго потока, ждет 3 секунды,
освобождает блокировку на чтение и
завершает работу.


Slide 33

Пример






Второй поток делает попытку получить
блокировку на запись (которую он может
получить, когда первый поток снимет
блокировку на чтение).
Однако первый поток послал сигнал на
отмену второго, что и произойдет после его
разблокирования.
Если первый поток не снимет блокировку (test
1), то второй не будет завершен и программа
зависнет.


Slide 34

Пример


В последнем листинге приведен пример
использования
блокировки,
не
приостанавливающих
выполенеие
вызвавшего их потока. Если глобальные
переменные заблокированы, сделавший это
поток отменяется. При этом происходит
сброс блокировок.