Sicherlich nicht. Aber mit der Programmiersprache Modula ist es möglich, aus ihm so etwas wie einen Quasi-Parallelrechner zu machen. Zwei scheinbar wenig benutzte Routinen aus dem Sprachumfang von Modula 2 sind nötig, um mehrere Dinge gleichzeitig, oder - um es genauer zu sagen - quasigleichzeitig ablaufen zu lassen.
Dabei ist dieses Konzept der quasigleichzeitig ablaufenden Programme auf dem ST schon längst verwirklicht, zumal bis zu sechs Accessories und ein beliebiges weiteres Programm quasigleichzeitig ablaufen können. Wartet eines der sieben Programme auf einen Event, so dürfen die anderen währenddessen weiterarbeiten.
Quasigleichzeitig arbeitende Routinen in einem einzigen Programm hingegen findet man kaum. 1st Word Plus ist ein Beispiel für diese wenigen Programme. Der Druck- und Ediervorgang laufen getrennt nebeneinander ab.
Wenden wir uns wieder Modula 2 zu. Wie schon erwähnt, ist es möglich, mit zwei Standardroutinen quasigleichzeitige Prozesse oder Coroutinen einzurichten. Dies ist einerseits Newprocess und andererseits Transfer.
Newprocess dient zur Einrichtung von Coroutinen, Transfer zum Umschalten zwischen der Coroutinen.
Newprocess werden als Parameter eine parameterlose Prozedur, eine Adresse für die lokalen Variablen der Coroutine und die Länge dieses Speicherplatzes übergeben. Zurückgegeben wird die Adresse der neuen Coroutine. Transfer übergibt man die Adresse der Quell- und der Zielroutine. Damit wären schon beide Routinen eingeführt.
Grundsätzlich werden die folgenden Schritte ausgeführt:
Das Programmbeispiel verdeutlicht die Wirkung der Coroutinen. Der Bildschirm wird in zwei Hälften unterteilt. Für die linke Hälfte ist die Prozedur Proc1 und für die rechte Proc2 zuständig. Nach dem Start des Beispielprogrammes werden beide Hälften des Bildschirms quasigleichzeitig benutzt. Es entstehen fast gleichzeitig beide Linienmuster! Daß dieses nur fast gleichzeitig abläuft, kann man höchstens daran merken, daß der Musteraufbau geringfügig langsamer abläuft als bei einer einzelnen Routine.
Neben der Umschalterei zwischen den Coroutinen wurden noch zwei weitere Variablen eingeführt: Status1 und Status2. Sie sollen den Status der beiden Routinen beinhalten. Ist ein Status wert TRUE, bedeutet das, daß die entsprechende Coroutine noch ausgeführt werden kann. Somit kann es nicht zu Fehlern bei einem unnötigen Aufruf der anderen Coroutine kommen, denn Coroutinen können grundsätzlich nur mit Transfer gestartet und auch wieder verlassen werden!
Noch ein Hinweis zu eigenen Projekten. Ein Austausch von Werten zwischen den Coroutinen ist beispielsweise über globale Variablen möglich. Dabei ist aber unbedingt darauf zu achten, daß nicht eine Coroutine einen globalen Wert verändert, den die andere noch in seinem Ursprungszustand benötigt.
Literatur:
Niklaus Wirth, Programmieren in Modula-2, Springer 1985
(* Modul zur Demonstration der Routinen NEWPROCESS und TRANSFER *)
(* Geschrieben mit Megamax Modula 2. dr *)
MODULE NewProcessExp;
FROM GrafBase IMPORT Pnt;
FROM GEMEnv IMPORT InitGem,ExitGem,RC, CurrGemHandle, DeviceHandle,GemHandle;
FROM VDIControls IMPORT ClearWorkstation;
FROM VDIOutputs IMPORT Line;
FROM SYSTEM IMPORT NEWPROCESS,TRANSFER,ADR,ADDRESS;
TYPE Buffer = ARRAY [0..1023] OF CHAR; (* Puffer für lok. Var. *)
VAR BufferProc1,BufferProc2 : Buffer; (* Puffer *)
Process0,Process1,Process2 : ADDRESS; (* Process-Adresse *)
StatusProc1,StatusProc2, (* Status aktiv/passiv *)
success : BOOLEAN;
Device : DeviceHandle;
gemHandle : GemHandle;
(* Procedure 1 *)
PROCEDURE Proc1;
VAR j : CARDINAL;
BEGIN
j:=0; (* Linienmuster *)
REPEAT
Line(Device,Pnt(0,0),Pnt(319,j));
IF StatusProc2 THEN
TRANSFER(Process1,Process2) (* -> anderer Process *)
END;
INC(j, 2)
UNTIL j>399;
j:=0; (* Linienmuster *)
REPEAT
Line(Device,Pnt(319,399),Pnt(0,399-j));
IF StatusProc2 THEN
TRANSFER(Process1,Process2) (* -> anderer Process *)
END;
INC(j,2)
UNTIL j>3 99;
StatusProc1:=FALSE; (* Proc1 passiv *)
TRANSFER(Process1,Process0) (* -> zurück *)
END Proc;
(* Procedure 2 *)
PROCEDURE Proc2;
VAR i : CARDINAL;
BEGIN
i:=0; (* Linienmuster *)
REPEAT
Line(Device,Pnt(320+i,0),Pnt(639-i, 399) ) ;
IF StatusProc1 THEN
TRANSFER(Process2,Process1) (* -> anderer Process *)
END;
INC(i,2)
UNTIL i>319;
i:=0; (* Linienmuster *)
REPEAT
Line(Device,Pnt(320,i),Pnt(639,399-i));
IF StatusProc1 THEN
TRANSFER(Process2,Process1) (* -> anderer Process *)
END;
INC (i,2)
UNTIL i>399;
StatusProc2:=FALSE; (* Proc2 passiv *)
TRANSFER(Process2,Process1) (* -> zurück *)
END Proc2;
BEGIN
InitGem(RC,Device,success); (* Initialisierung *)
IF success THEN
gemHandle:=CurrGemHandle();
(* neue Coroutinen einrichten *)
NEWPROCESS(Proc1,ADR(BufferProc1),SIZE(BufferProc1),Process1);
NEWPROCESS(Proc2,ADR(BufferProc2),SIZE(BufferProc1),Process2);
ClearWorkstation(Device); (* Bildschirm löschen *)
Process0 :=NIL; (* Coroutine0 = NIL *)
StatusProc1:=TRUE; (* Status Process1 aktiv *)
StatusProc2:=TRUE; (* Status Process2 aktiv *)
TRANSFER(Process0,Process1); (* Process1 starten *)
ExitGem(gemHandle) (* Ende *)
END
END NewProcessExp.