Замки и сигналы

Замки

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

>('x  set 0)
0
>(100 for i ('x set (x  + 1)))  ; возникнет 100 параллельных процессов
nil
>x
7

  Данную возможность можно получить используя замки:

>('x  set 0)
0
>('xlock  set ('lock  newobject))
Lock
>(100 for i (xlock  progn ('x set (x  + 1))))
nil
>x
100

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

Сигналы

  Сигналы необходимы для общения нескольких процессов. Если процесс ожидает выполнения некоторой задачи, то выполнение циклической проверки очень сильно и максимально нагружает процессоры бесполезным трудом.
  При составлении программы сначала устанавливается замок. Под защитой замка проверяется есть-ли необходимость использовать сигналы. Затем используется очень простая функция wait. Эта функция открывает замок и ждёт сигнал от других процессов. После получения сигнала замок снова устанавливается.
  При пошаговом (без параллелизма) программировании программы постоянно проверяют некоторые условия. Это усложняет программирование и очень сильно подвержено ошибкам. При использовании сигналов программа получается очень проста и эффективна.
  Приведём очень простой пример:

('signal  set ('signal  newobject))
('lock  set ('lock  newobject))
('s set (10 copy))
(nil  parallel
  ; основная задача
  (lock progn
    (cout writeln "жду...")
    (signal wait  lock)
    (cout writeln "OK"))
  ; другая задача
  (nil  progn
    (nil  while (s  > 0)
      (lock progn
        (cout writeln "работаю")
        (s  -=  1)))
    (cout writeln "посылаю сигнал")
    (signal send)))

  Порядок подачи, ожидания сигнала не имеет значения. Посланные сигналы не пропадают.

Профессиональный подход

  В крупных проектах иногда есть необходимость обрабатывать большой источник информации, не модифицируя его. И желательно дать возможность параллельным процессам выполнятся одновременно. Если появилась необходимость модифицировать информацию то нужно чтобы работал один процесс.
  Этот сказочный процесс можно сделать усилиями замков и сигналов. Сделаем класс passage который организует весь процесс работы.

(('passage  defclass) defvar
  lockEntrance
  lockOutlet
  signalShout
  guests
  flagToShout)
('passage defmethod passage ()
  (nil  setq
    lockEntrance  ('lock  newobject)
    lockOutlet  ('lock  newobject)
    signalShout ('signal  newobject)
    guests  (0  copy)
    flagToShout false))
('passage defmacro  Pass  (env  &rest body)
  (lockEntrance progn
    (lockOutlet progn
      (guests +=  1)))
  (nil  let ((result  (env  eval  `(nil progn ,@body))))
    (lockOutlet progn
      (nil  if  ((guests  -=  1)  = 0)
        (nil  when  flagToShout
          ('flagToShout set false)
          (signalShout  send))))
    `',result))
('passage defmacro  Lock  (env  &rest body)
  (lockEntrance progn
    (lockOutlet progn
      (nil  when  (guests <>  0)
        ('flagToShout set true)
        (signalShout  wait  lockOutlet)))
    `',(env eval  `(nil progn ,@body))))