Элемент кортежа может быть произвольным объектом; это работает, потому что
drb
умеет выполнять маршалинг и демаршалинг объектов Ruby. (Конечно, необходимо либо включить модуль
DRbUndumped
, либо сделать определения объектов доступными серверу.)
Пространство объектов создается методом
new
:
require 'rinda/tuplespace'
ts = Rinda::TupleSpace.new
# ...
Поэтому
сервер выглядит так:
require 'rinda/tuplespace'
ts = Rinda::TupleSpace.new
DRb.start_service("druby://somehost:9000", ts)
gets # Нажать Enter для завершения сервера.
А клиент — так:
require 'rinda/tuplespace'
DRb.start_service
ts = DRbObject.new(nil, "druby://somehost:9000")
# ...
К пространству кортежей в Rinda применимы пять операций:
read
,
read_all
,
write
,
take
и
notify
.
Операция чтения
read
позволяет получить один кортеж. Но способ идентификации кортежа не вполне очевиден: необходимо задать кортеж, соответствующий искомому; при этом
nil
соответствует любому значению.
t1 = ts.read [:Sum,nil] # Может извлечь, например, [:Sum, 14].
Обычно операция
read
блокирует выполнение программы (для синхронизации). Чтобы быстро проверить существование кортежа, можно выполнить неблокирующее чтение, задав нулевой тайм-аут:
t2 = ts.read [:Result,nil],0 # Возбуждает исключение, если кортеж
# не существует.
Если мы точно знаем или предполагаем, что образцу будет соответствовать не один, а несколько кортежей, можно воспользоваться методом
read_all
, который возвращает массив:
tuples = ts.read_all [:Foo, nil, nil]
tuples.each do |t|
# ...
end
Метод
read_all
не принимает второго параметра. Он всегда блокирует программу, если не найдено ни одного подходящего кортежа.
Операция
take
— это чтение, за которым следует удаление. Иными словами, метод
take
удаляет кортеж из пространства кортежей и возвращает его вызывающей программе:
t = ts.take [:Sum, nil] # Кортежа больше нет в пространстве кортежей.
Может возникнуть вопрос, почему не существует явного способа удаления. Надо полагать, что этой цели служит метод take.
Метод
write
помещает кортеж в пространство кортежей. Второй параметр показывает, сколько секунд кортеж может существовать, прежде чем система сочтет, что срок его хранения истек. (По умолчанию его значение равно
nil
, то есть срок хранения не ограничен.)
ts.write [:Add, 5, 9] # Хранить "вечно".
ts.write [:Foo, "Bar"], 10 # Хранить 10 секунд.
Здесь
уместно будет сказать несколько слов о синхронизации. Предположим, что два клиента пытаются одновременно забрать (
take
) один и тот же кортеж. Одному это удастся, а другой будет заблокирован. Если первый клиент затем изменит кортеж и запишет (
write
) его обратно в хранилище, то второй получит модифицированную версию. Можно считать, что операция «обновления» — это последовательность
take
и
write
, которая не приводит к потере данных. Конечно, как и при любом варианте многопоточного программирования, нужно позаботиться о том, чтобы не возникали тупиковые ситуации.
Метод
notify
позволяет следить за пространством кортежей и получать уведомления, когда над интересующим вас кортежем была выполнена какая-то операция. Этот метод возвращает объект
NotifyTemplateEntry
и может наблюдать на операциями четырех видов:
•
write
;
•
take
;
• удаление (когда истекает срок хранения кортежа);
• закрытие (когда истекает срок хранения объекта
NotifyTemplateEntry
).
Поскольку операция чтения ничего не изменяет, то система не поддерживает уведомлений о чтениях. В листинге 20.4 приведен пример использования notify.