Monitor

Die Verwendung von Monitoren und Signalen ist eine Möglichkeit, Threads (Coroutinen) auf Programmiersprachen-Niveau zu synchronisieren.

Ein Monitor ist dabei ein spezielles Modul bzw. eine Klasse, das / die mehrere Prozeduren und Datenobjekte (Instanzvariable) definiert. Es darf zu jedem Zeitpunkt höchstens ein Thread im Monitor aktiv sein.

Die Threads können über gemeinsame Variable oder über sogenannte Signale miteinander kommunizieren.

Das Monitor-Konzept ist bei zwei Programmiersprachen verwirklicht worden: Modula-2 und Java.


Monitore in Modula-2

Threads werden in der Modula-2-Dokumentation als Coroutinen bezeichnet, in diesem Text bleibt es aber bei der Bezeichnung Thread. Monitore werden mit Modulen identifiziert, die zusätzlich eine Prioritätsangabe an ihren Namen angehängt bekommen, wie z.B. [1].

Ein Signal ist ein abstrakter Datentyp mit den Operationen:

Init(VAR s:SIGNAL)
SEND(VAR s:SIGNAL)
WAIT(VAR s:SIGNAL)
Awaited(s:SIGNAL):BOOLEAN

Mit WAIT kann ein Thread auf ein Signal warten, das ein anderer Thread mittels SEND absenden kann. Wartet kein Thread auf das Signal, ist die Operation SEND wirkungslos. SEND und WAIT haben normalerweise zur Folge, dass der aufrufende Thread den Monitor verlässt und ein anderer Thread aktiv wird. Im Falle eines SEND wird der Thread aktiv, der am längsten auf das Signal wartet. Im Falle eines WAIT wird ein Thread aktiv, der auf kein Signal wartet.

Diese Strategie lässt sich effizient realisieren, indem (1) ein Ring (zirkuläre Warteschlange) aller Threads verwaltet wird und (2) ein Signal als Warteschlage der auf das Signal wartenden Threads implementiert wird.
 
TYPE SIGNAL = POINTER TO ThreadDescriptor;

     ThreadDescriptor = RECORD
                          next:SIGNAL;     (* Ring der Threads *)
                          queue:SIGNAL;    (* erster wartender Thread *)
                          ready:BOOLEAN;   (* wartend? *)
                          state:THREADSTATE;
                        END;

VAR current:SIGNAL; (* aktueller Thread *)

PROCEDURE StartProcess(P:PROC; n:CARDINAL);
VAR s:SIGNAL;
    wsp:ADDRESS;
BEGIN
  s:=current;
  ALLOCATE(wsp, n);
  WITH current^ DO
    next:=s^.next;
    s^.next:=current;
    queue:=NIL;
    ready:=TRUE;
  END;
  NEWPROCESS(P, wsp, n, current^.state); (* erzeugt den neuen Thread *)
  TRANSFER(s^.state, current^.state);    (* und macht ihn aktiv *)
END StartProcess;

PROCEDURE SEND(VAR s:SIGNAL);
VAR inactivated:SIGNAL;
BEGIN
  IF s <> NIL THEN
    inactivated:=current;
    current:=s;
    WITH current^ DO
      (* verkürze die Schlange der auf die Semaphore wartenden Threads *)
      s:=queue;
      queue:=NIL;
      ready:=TRUE;
    END;
    (* mache den ersten wartenden zum aktuellen Thread *)
    TRANSFER(inactivated^.state, current^.state);
  END;
END SEND;

PROCEDURE WAIT(VAR s:SIGNAL);
VAR s0,s1:SIGNAL;
BEGIN
  IF s = NIL THEN
    s:=current;
    s^.last:=s;
  ELSE
    s0:=s;
    s1:=s0^.queue;
    WHILE s1 <> NIL DO
      s0:=s1;
      s1:=s0^.queue;
    END;
    s0^.queue:=current; (* aktuellen Thread am Ende einfügen *)
  END;
  s0:=current;
  REPEAT current:=current^.next; UNTIL current^.ready;
  IF current = s0 THEN
    (* Deadlock *)
    HALT;
  END;
  s0^.ready:=FALSE;
  TRANSFER(s0^.state, current^.state);
END WAIT;

PROCEDURE Awaited(s:SIGNAL):BOOLEAN;
BEGIN
  RETURN s <> NIL;
END Awaited;

PROCEDURE Init(VAR s:SIGNAL);
BEGIN
  s:=NIL;
END Init;

Bei einem SEND wird dann ein Thread aus der Warteschlange entnommen, der aktuelle Thread suspendiert und der entnommene Thread fortgesetzt. Bei einem WAIT wird der aktuelle Thread in die Warteschlange eingefügt, dann suspendiert und der nächste nicht wartender Thread aus dem Ring fortgesetzt. Findet sich kein rechenbereiter Thread, dann liegt ein Deadlock vor.
 

Monitore in Java

Hier sind Monitore Klassen, bei denen die Methoden, in der sich nur ein Thread aufhalten darf, durch das synchronized Schlüsselwort gekennzeichnet sind.

Jedes Java-Objekt darf als Signal agieren, indem seine wait- bzw. notify-Methode aufgerufen wird. Die wait-Methode darf nur innerhalb eines synchronized-Blocks aktiviert werden, sonst resultiert eine IllegalMonitorStateException.