next up previous contents
След.: Критические секции Выше: Операторы синхронизации Пред.: Оператор SYNC IMAGES   Содержание


Операторы LOCK и UNLOCK

Замки (locks) обеспечивают механизм контроля доступа к данным, которые определяют или используют более, чем один образ. Замок -- это скалярная переменная производного типа LOCK_TYPE, который определен во встроенном модуле ISO_FORTRAN_ENV. Замок должен быть комассивом или подобъектом комассива. Он имеет два состояния: открыт и закрыт. Состояние «открыт» описывается значением по умолчанию. Все прочие значения означают «закрыт». Единственный способ изменить состояние -- это вызвать оператор LOCK или UNLOCK. Например, если замок является формальным аргументом процедуры или подобъектом такового, аргумент не должен иметь атрибут INTENT(OUT). Если переменная-замок закрыта, открыть ее может только тот образ, что ее закрыл.

Синтаксис операторов LOCKUNLOCK):

LOCK(замок [спецификаторы])

где замок имеет тип LOCK_TYPE, а спецификатор ACQUIRED_LOCK=имя (только для LOCK и необязателен) задает переменную логического типа (она становится истиной, если замок закрыт, и ложью, если он уже закрыт другим образом). Еще два возможных спецификатора описаны в конце параграфа.

Если замок открыт образом $ M$ вызовом UNLOCK и позже закрыт образом $ P$ LOCK, то все сегменты $ M$ до UNLOCK предшествуют все сегментам $ P$ после LOCK. Если вызов LOCK не изменил статуса замка, порядок сегментов не определен.

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

Возникает ошибка, если LOCK пытается запереть уже запертый ранее тем же образом замок, а также -- если UNLOCK, пытается отпереть замок, который не был ранее заперт этим же образом.

Пример: Использование LOCK и UNLOCK для управления стеками.

MODULE STACKMANAGER

USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY: LOCK_TYPE

TYPE TASK

...

END TYPE

TYPE(LOCK_TYPE), PRIVATE :: STACK_LOCK[*]

TYPE(TASK), PRIVATE :: STACK(100)[*]

INTEGER, PRIVATE :: STACK_SIZE[*]



CONTAINS



SUBROUTINE GET_TASK(JOB)

! Получить задание из стека

TYPE(TASK),INTENT(OUT) :: JOB

LOCK(STACK_LOCK)

JOB=STACK(STACK_SIZE)

STACK_SIZE=STACK_SIZE-1

UNLOCK(STACK_LOCK)

END SUBROUTINE GET_TASK



SUBROUTINE PUT_TASK(JOB,IMAGE)

! Положить задание в стек образа

TYPE(TASK),INTENT(IN) :: JOB

INTEGER,INTENT(IN) :: IMAGE

LOCK(STACK_LOCK[IMAGE])

STACK_SIZE[IMAGE]=STACK_SIZE[IMAGE]+1

STACK(STACK_SIZE[IMAGE])[IMAGE] = JOB

UNLOCK(STACK_LOCK[IMAGE])

END SUBROUTINE PUT_TASK



END MODULE STACK_MANAGER

Пример иллюстрирует использование операторов LOCK и UNLOCK для управления стеками. Каждый образ имеет свой стек; каждый образ может добавлять задания в любой стек. Если оператор LOCK на образе $ P$ запирает переменную-замок, ранее закрытую другим образом $ Q$ , то образ $ P$ ждет, пока $ Q$ не откроет замок. Результатом этого в данном примере является тот факт, что процедура GET_TASK будет ждать, пока другой образ закончит класть задание в стек, а PUT_TASK будет ждать, если GET_TASK вынимает задание из стека или другой образ выполняет PUT_TASK на этом же образе.



Ilya A. Chernov 2012-12-19
X