Семафоры
Семафор связан с целочисленным значением, которое никогда не должно опускаться ниже нуля. Для семафора sem
можно выполнять две операции: увеличивать значение семафора на единицу с помощью post(sem)
и уменьшать значение семафора на единицу с помощью wait(sem)
. Если значение семафора в данный момент равно нулю, вызов wait(sem)
будет блокироваться до тех пор, пока значение не станет больше нуля.
Существует два вида семафоров: именованные и анонимные. Именованные семафоры идентифицируются по их имени, которое представляет собой строку вида "/somename"
. Анонимные семафоры поддерживаются объектами памяти (обычно общей памяти), обеспечивающими необходимое хранение. В пакете Julia InterProcessCommunication
семафоры являются экземплярами Semaphore{T}
, где T
представляет собой String
для именованных семафоров и тип объекта резервной памяти для анонимных семафоров.
Именованные семафоры
Именованные семафоры идентифицируются по их имени, которое представляет собой строку вида "/somename"
. Новый именованный семафор, идентифицируемый строкой name
, создается следующим образом:
Semaphore(name, value; perms=0o600, volatile=true) -> sem
при этом создается новый именованный семафор с начальным значением value
и возвращается экземпляр Semaphore{String}
. Для указания прав доступа (значение по умолчанию предоставляет права на чтение и запись для вызывающего объекта) можно использовать ключевое слово perms
. Ключевое слово volatile
указывает, должен ли семафор быть отсоединен после финализации возвращаемого объекта.
Другой процесс (или поток) может открыть существующий именованный семафор, вызвав следующее:
Semaphore(name) -> sem
при этом выдается экземпляр Semaphore{String}
.
Чтобы отсоединить (удалить) постоянный именованный семафор, просто сделайте следующее:
rm(Semaphore, name)
Если семафор не существует, ошибка игнорируется. Однако для других ошибок выводится ошибка SystemError
.
Для максимальной гибкости экземпляр именованного семафора также может быть создан с помощью следующего:
open(Semaphore, name, flags, mode, value, volatile) -> sem
где для flags
могут быть заданы биты IPC.O_CREAT
и IPC.O_EXCL
, mode
определяет предоставленные права доступа, value
является начальным значением семафора, а volatile
представляет собой логическое значение, указывающее, должен ли семафор быть отсоединен после финализации возвращаемого объекта sem
. Значения mode
и value
игнорируются, если открыт существующий именованный семафор.
Анонимные семафоры
Анонимные семафоры поддерживаются объектами памяти (обычно общей памяти), обеспечивающими необходимое хранение.
Новый анонимный семафор создается следующим образом при вызове:
Semaphore(mem, value; offset=0, volatile=true) -> sem
при этом инициализируется анонимный семафор, поддерживаемый объектом памяти mem
с начальным значением value
, и возвращается экземпляр Semaphore{typeof(mem)}
Для указания адреса (в байтах) данных семафора относительно pointer(mem)
можно использовать ключевое слово offset
. Ключевое слово volatile
указывает, должен ли семафор быть уничтожен после финализации возвращаемого объекта.
Количество байтов, необходимое для хранения анонимного семафора, задается с помощью sizeof(Semaphore)
, а анонимный семафор должен быть выровнен в памяти на величину, кратную размеру слова в байтах (то есть Sys.WORD_SIZE >> 3
). Объекты памяти, используемые для хранения анонимного семафора, должны реализовывать два метода: pointer(mem)
и sizeof(mem)
для получения базового адреса (как экземпляра Ptr
) и размера (в байтах) связанной памяти соответственно.
Другой процесс (или поток) может использовать существующий (инициализированный) анонимный семафор, вызвав следующее:
Semaphore(mem; offset=0) -> sem
где mem
— объект памяти, обеспечивающий хранение семафора в относительном расположении, заданном ключевым словом offset
(по умолчанию — нуль). Возвращаемое значение является экземпляром Semaphore{typeof(mem)}
.
Операции с семафорами
Значение и размер семафора
Чтобы запросить значение семафора sem
, выполните следующее:
sem[]
Однако следует помнить, что к моменту возвращения результата значение семафора уже может измениться. Минимальные и максимальные значения, которые может принимать семафор, задаются следующим образом:
typemin(Semaphore)
typemax(Semaphore)
Чтобы выделить память для анонимных семафоров, нужно знать количество байтов, необходимых для хранения семафора. Оно задается следующим образом:
sizeof(Semaphore)
Размещение и ожидание
Чтобы разблокировать семафор sem
, вызовите следующее:
post(sem)
при этом значение семафора увеличивается на единицу. Если значение семафора после этого станет больше нуля, будет пробужден другой процесс или поток, заблокированный в вызове wait
для этого семафора.
Блокировка семафора sem
осуществляется с помощью следующего:
wait(sem)
при этом значение семафора sem
уменьшается на единицу. Если значение семафора больше нуля, уменьшение продолжается и функция немедленно возвращается. Если в данный момент семафор имеет нулевое значение, вызов блокируется до тех пор, пока либо не появится возможность выполнить уменьшение (т. е. значение семафора станет больше нуля), либо обработчик сигнала не прервет вызов (в этом случае будет выдан экземпляр исключения InterruptException
). В случае непредвиденной ошибки может возникнуть ошибка SystemError
.
Чтобы попробовать заблокировать семафор sem
без блокировки, сделайте следующее:
trywait(sem) -> boolean
при этом будет предпринята попытка немедленного уменьшения значения (блокировки) семафора с возвращением true
в случае успеха. Если уменьшение не может быть выполнено сразу же, возвращается false
. В случае прерывания или непредвиденной ошибки возникает исключение (InterruptException
или SystemError
соответственно).
Наконец, вызов:
timedwait(sem, secs)
уменьшает значение семафора sem
(блокирует семафор). Если значение семафора больше нуля, уменьшение продолжается и функция немедленно возвращается. Если в данный момент семафор имеет нулевое значение, вызов блокируется до тех пор, пока либо не появится возможность выполнить уменьшение (т. е. значение семафора станет больше нуля), либо не истечет ограничение в заданное количество secs
секунд (в этом случае будет выдан экземпляр ошибки TimeoutError
), либо обработчик сигнала не прервет вызов (в этом случае будет выдан экземпляр исключения InterruptException
).