Essende Philosophen

Philosophen haben ein angenehmes Leben: sie denken, davon werden sie hungrig. Dann essen sie. Danach beginnt alles von vorne mit Denken. Insgesamt gibt es fünf Philosophen, die um einen runden Tisch sitzen. Leider gibt es nur fünf Gabeln, eine zwischen zwei benachbarten Philosophen. Zum Essen braucht ein Philosoph die Gabeln zu seiner Rechten und Linken. Deshalb können nicht alle Philosophen gleichzeitig essen, sondern höchstens zwei und die können nicht nebeneinander sitzen. Das folgende Programm simuliert die Philosophen.

Die Funktion think gibt den Namen des Philosophen zusammen mit dem Hinweis, dass der Philosoph nun denkt, auf einem Kanal aus.

(defproc think (name out)
  (send-on-channel out (concatenate name " is thinking") nil))

Die Funktion eat nimmt die beiden Gaben und gibt den Namen des essenden Philosophen aus und wartet dann einige Millisekunden.

(defproc eat (name first-fork second-fork out)
  (with-lock first-fork
    (with-lock second-fork
      (progn
        (send-on-channel out (concatenate name " is eating") nil)
        (wait)))))

(defproc wait ()
  (receive-from-channels nil (+ 3 (random 4))))

Wie schon einleitend beschrieben, besteht das Leben eines Philosophen aus der Wiederholung von Denken und Essen.

(defproc live (name first-fork second-fork out)
  (loop
    (when (closed-channel? out)
      (return nil))
    (think name out)
    (eat name first-fork second-fork out)))
    
Und es gibt fünf Philosophen und fünf Gabeln. Die Funktion make-philosophers erzeugt diese und liefert den Kanal, in den die Nachrichten geschrieben werden.

(defproc make-philosophers ()
   (let
    ((fork-1 (make-lock))
     (fork-2 (make-lock))
     (fork-3 (make-lock))
     (fork-4 (make-lock))
     (fork-5 (make-lock))
     (out (make-channel 1)))
    (progn
      (go (live "andi" fork-1 fork-2 out))
      (go (live "berti" fork-2 fork-3 out))
      (go (live "conni" fork-3 fork-4 out))
      (go (live "det" fork-4 fork-5 out))
      (go (live "emil" fork-1 fork-5 out)) #| avoid deadlock by resource ordering |#
      out)))

Um die Philosophen zu beobachten, benutzt man die Funktion take. Diese entnimmt eine Anzahl von Nachrichten aus einem Kanal und fügt sie zu einer Liste zusammen.

(defproc take (in count)
  (let
    ((result nil))
    (dotimes (index count (reverse result))
      (push (receive-from-channels (list in) nil) result))))

So liefert der Ausdruck

(let
  ((p (make-philosophers)))
  (prog1
    (take p 50)
    (close-channel p)))

die ersten 50 Nachrichten der Simulation.


dining-philosophers.sheet.xml