home *** CD-ROM | disk | FTP | other *** search
- {$R-,S-,I-,D-,F-,V-,B-,N-,L-}
- {$M 16384,0,655360 }
- PROGRAM ProducerConsumer;
-
- { Darstellung des Producer-Consumer-Problems am Beispiel einer
- Tastaturein-/ausgabesteuerung.
-
- Funktionsbeschreibung:
- Eingabe von Zeichen über die Tastatur und Ausgabe auf dem Bildschirm.
- ESC bewirkt ein Beenden des Programmes.
- ^S suspendiert die Ausgabe bis zum Erhalt von ^Q; in der Zwischenzeit
- eingegebenene Zeichen werden im Ringpuffer abgelegt, jedoch noch
- nicht angezeigt.
- ^Q reaktiviert die Ausgabe; der gespeicherte Pufferinhalt wird augen-
- blicklich dargestellt.
-
-
- Stand: 01/89
- Autor: Christian Philipps Software-Technik
- Düsseldorfer Str. 316
- 4130 Moers 1
- }
-
- USES Crt, CpMulti;
-
- CONST RBuffSize = 36; {Größe des Ringpuffers}
- CritLin = 15;
- CritCol = 51;
- FullLin = 15;
- FullCol = 34;
- EmptyLin = 15;
- EmptyCol = 42;
- PEndLin = 15;
- PEndCol = 12;
- OutLin = 15;
- OutCol = 24;
-
- VAR RBuff : RECORD
- Buff : ARRAY[0..RBuffSize] OF CHAR;
- {Ringpuffer; 1 Element ver-
- schenkt, dadurch leichtere
- Handhabung.}
- Critical : Pointer; {Semaphore für Synchronisa-
- tion des Zugriffs}
- Full : Pointer; {Semaphore zur Zählung beleg-
- ter Pufferplätze}
- Empty : Pointer; {Semaphore zur Zählung freier
- Pufferplätze}
- Head : Byte; {Head- und Tailpointer}
- Tail : Byte;
- END;
- OutputSem : Pointer; {Semaphore zur Ausgabesteuerung}
- ProgramEnd: Pointer; {Signalisiere Programmende}
- ConsumerNo, {Task-IDs}
- ProducerNo: TaskNoType;
-
- {-----------------------------------------------------------------------------}
-
- FUNCTION NextSlot(S:Byte):Byte;
-
- { Errechne nächste Rinpufferposition, die auf S folgt }
-
- BEGIN {NextSlot}
- NextSlot := Succ(S) MOD RBuffSize;
- END; {NextSlot}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE WriteCharXY(X,Y:Byte; C:Char);
-
- { Ausgabe eines Zeichens an der Position X,Y. Hierbei wird durch entsprechende
- CPU-Blockierung sichergestellt, daß niemals die Sequenz GotoXY und Write
- durch einen Task-Weschesl unterbrochen werden kann! }
-
- BEGIN {WriteCharXY}
- BindCPU;
- GotoXY(X,Y);
- Write(C);
- ReleaseCPU;
- END; {WriteCharXY}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE WriteByteXY(X,Y,B:Byte);
-
- { Ausgabe eines Byte-Wertes an der Position X,Y 2-stellig.
- Erläuterung siehe WriteCharXY }
-
- BEGIN {WriteByteXY}
- BindCPU;
- GotoXY(X,Y);
- Write(B:2);
- ReleaseCPU;
- END; {WriteByteXY}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE Status;
-
- { Einblenden des Task-Status }
-
- BEGIN {Status}
- BindCPU;
- GotoXY(65,9);
- Write(StateText[GetTaskState(ConsumerNo)]:10);
- GotoXY(65,22);
- Write(StateText[GetTaskState(ProducerNo)]:10);
- ReleaseCPU;
- END; {Status}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE SW(S:Pointer; c,l:byte);
-
- { Aufruf SemWait mit Visualisierung }
-
- BEGIN {SW}
- WriteByteXY(C,L,SemGetSignals(S));
- SemWait(S);
- WriteByteXY(C,L,SemGetSignals(S));
- Status;
- END; {SW}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE SS(S:Pointer; c,l:byte);
-
- { Aufruf SemSignal mit Visualisierung }
-
- BEGIN {SS}
- SemSignal(S);
- WriteByteXY(C,L,SemGetSignals(S));
- Status;
- END; {SS}
-
- {-----------------------------------------------------------------------------}
-
- FUNCTION RBuffPut(C:Char):BOOLEAN;
-
- { Stelle ein Zeichen im Ringpuffer ab. True, falls erfolgreich; False,
- wenn Pufferüberlauf. Da die Eingabesteuerung das Verhalten der Ausgabe-
- Task beeinflußt (^S und ^Q), darf bei der Einspeisung von Zeichen in den
- Ringpuffer niemals die Eingabesteuerung für längere Zeit blockiert werden.
- Letzteres wäre aber der Fall, wenn in jedem Fall auf das Freiwerden eines
- Pufferslots gewartet würde. Ist nämlich gerade die Ausgabesteuerung nach
- Empfang eines ^S nicht in der Lage, einen Slot zu leeren und die Eingabe-
- steuerung blockiert beim Wait auf einen Speicherplatz, so hängt das Ge-
- samtsystem unwiederruflich!!
- ACHTUNG!! Man beachte die Positionierung der Wait-Aufrufe auf die Synchro-
- nisations-Semaphore Critical!! Da eine Blockierung durch Wait auf Empty
- abhängig vom Signal-Count der Empty-Semaphore ist, jedoch THEORETISCH
- in der Zeit zwischen der Abfrage des Signal-Count und dem Wait auf Empty
- eine andere Task den letzten Slot des Puffer belegt haben könnte,
- muß durch eine entsprechende Abgrenzung des kritischen Bereiches sicher-
- gestellt sein, daß bis zur endgültigen Belegung des Pufferplatzes, keine
- andere Task Zugang zum Ringpuffer erhalten kann.}
-
- BEGIN {RBuffPut}
- WITH RBuff DO
- BEGIN
- SW(Critical,CritCol,CritLin); {exclusiver Pufferzugriff}
- IF SemGetSignals(Empty) = 0 {Puffer voll}
- THEN RBuffPut := False {Verhindere Blockierung}
- ELSE BEGIN
- RBuffPut := True;
- SW(Empty,EmptyCol,EmptyLin); {belege einen Slot}
- Buff[Tail] := c; {stelle Zeichen ab}
- WriteCharXY(21+Tail,19,' ');
- IF C = #13
- THEN WriteCharXY(21+Tail,21,#188)
- ELSE WriteCharXY(21+Tail,21,c);
- Tail := NextSlot(Tail); {Headpointer auf nächsten Slot}
- WriteCharXY(21+Tail,19,#25);
- SS(Full,FullCol,FullLin); {1 Zeichen mehr im Puffer}
- END;
- SS(Critical,CritCol,CritLin); {Puffer freigeben}
- END;
- END; {RBuffPut}
-
- {-----------------------------------------------------------------------------}
-
- FUNCTION RBuffGet:Char;
-
- { Entnimm das erste Zeichen aus dem Ringpuffer und stelle es der Anwendung
- zur Verfügung. Ist der Puffer gerade leer, so warte auf das Eintreffen
- eines Zeichens }
-
- BEGIN {RBuffGet}
- WITH RBuff DO
- BEGIN
- SW(Full,FullCol,FullLin); {Fordere Zeichen an}
- SW(Critical,CritCol,CritLin); {exclusiver Zugriff}
- RBuffGet := Buff[Head]; {entnimm Zeichen}
- WriteCharXY(21+Head,23,' ');
- Head := NextSlot(Head); {aktualisiere Head-Pointer}
- WriteCharXY(21+Head,23,#24);
- SS(Critical,CritCol,CritLin); {Freigeben des Puffers}
- SS(Empty,EmptyCol,EmptyLin); {1 Slot mehr frei}
- END;
- END; {RBuffGet}
-
- {-----------------------------------------------------------------------------}
-
- {$F+}
- PROCEDURE Producer(P:Pointer);
-
- { Eingabesteuerung: Lies alle verfügbaren Zeichen von der Tastatur in den
- Ringpuffer ein. Wird ein ^S empfangen, so wird die Ausgabe von Zeichen
- bis zum Erhalt eines ^Q unterbunden }
-
- VAR C : Char;
- Display : Boolean;
- Col : Byte;
-
- BEGIN {Producer}
- Display := True; {Anzeige erlaubt}
- Col := 1;
- REPEAT {Endlosschleife}
- WHILE Keypressed DO
- BEGIN
- C := ReadKey;
- CASE C OF
- ^S: IF Display {sofern nicht bereis passiert}
- THEN BEGIN
- SW(OutputSem,OutCol,OutLin); {Ausgabe unterbinden}
- Display := False; {Zustand merken}
- END;
- ^Q: IF NOT Display {falls Ausgabe unterbunden}
- THEN BEGIN
- SS(OutputSem,OutCol,OutLin); {Ausgabe freigeben}
- Display := True; {neuen Status merken}
- END;
- ELSE {kein Sonderzeichen}
- BEGIN
- IF NOT RBuffPut(C)
- THEN BEGIN {Überlauf}
- BindCPU; {atomic action}
- GotoXY(34,18);
- TextBackground(White);
- TextColor(Black);
- Write(' Überlauf ');
- TextColor(White);
- TextBackground(Black);
- ReleaseCPU; {Ende atomic action}
- END;
- END;
- END; {Case}
- END;
- Sched; {alle Zeichen verarbeitet,
- gib Zeitscheibe auf}
- UNTIL False;
- END; {Producer}
-
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE Consumer(P:Pointer);
-
- { Dies ist die Task, die ständig Zeichen aus dem Ringpuffer entnimmt und
- auf dem Bildschirm ausgibt.
- Ist ein ^S von der Eingabesteuerung empfangen worden, so belegt diese die
- OutputSem-aphore und sorgt so für eine Blockierung der Ausgabe-Task beim
- nächsten Versuch ein Zeichen auf dem Bildschirm auszugeben.
- Bei Erhalt eines ^Q wir diese Semaphore wieder freigegeben.
- Wird aus dem Puffer das Zeichen ESC entnommen, so veranlaßt diese Task das
- Beenden des Programmes durch Incrementieren des Signal-Count der Semaphore
- ProgramEnd.
- Die Consumer-Task läuft mit höchster Priorität, da sie die meiste Zeit
- ohnehin auf Eingaben wartet und daher andere Tasks nicht behindert. Lie-
- gen jedoch Zeichen an, so werden diese im Interesse einer schnellen Re-
- aktion augenblicklich bearbeitet. }
-
- CONST MaxCols = 50;
-
- VAR C : Char;
- Col : Byte;
-
- BEGIN {Consumer}
- Col := 1;
- REPEAT {Endlosschleife}
- C := RBuffGet; {hole Zeichen}
- GotoXY(34,18); {lösche Überlauftext}
- Write(' ');
- IF C = #27
- THEN SS(ProgramEnd,PendCol,PendLin) {Ende des Programmes}
- ELSE BEGIN
- SW(OutPutSem,OutCol,OutLin); {warte auf Ausgabeerlaubnis}
- IF (Col >= MaxCols) OR (C=#13) {Display-Überlauf / Return}
- THEN BEGIN
- BindCPU; {kritischer Bereich}
- GotoXY(7,8);
- FOR Col := 1 TO MaxCols DO
- Write(' ');
- ReleaseCPU; {Ende des kritischen Bereiches}
- Col := 1;
- END;
- IF C <> #13 {Zeichenausgabe}
- THEN BEGIN
- WriteCharXY(6+Col,8,C);
- Inc(Col);
- END;
- SS(OutPutSem,OutCol,OutLin); {Sempahore wieder setzen}
- END;
- UNTIL False;
- END; {Consumer}
- {$F-}
-
- {-----------------------------------------------------------------------------}
-
- PROCEDURE DrawScreen;
-
- BEGIN {DrawScreen}
- ClrScr;
- BindCPU;
- GotoXY(15,1);
- Write('P R O Z E S S - S Y N C H R O N I S A T I O N');
- GotoXY(18,3);
- Write('Darstellung des Producer-Consumer Problems');
- GotoXY(24,4);
- Write('Autor: Christian Philipps 6/88');
- GotoXY(5,7);
- Write('┌───────────────────────────────────────────────────┐');
- GotoXY(5,8);
- Write('│ │ Consumer-Task');
- GotoXY(5,9);
- Write('└───────────────────────────────────────────────────┘');
- GotoXY(6,12);
- Write('┌────────────┬───────────┬──────┬───────┬──────────┐');
- GotoXY(6,13);
- Write('│ ProgramEnd │ OutputSem │ Full │ Empty │ Critical │ Semaphoren zur');
- GotoXY(6,14);
- Write('├────────────┼───────────┼──────┼───────┼──────────┤ Prozeß- und Zugiffs-');
- GotoXY(6,15);
- Write('│ │ │ │ │ │ synchronisation');
- GotoXY(6,16);
- Write('└────────────┴───────────┴──────┴───────┴──────────┘');
- GotoXY(5,19);
- Write('Head-Pointer');
- GotoXY(20,20);
- Write('┌────────────────────────────────────┐');
- GotoXY(5,21);
- Write('Ringpuffer -> │ │ Producer─Task');
- GotoXY(20,22);
- Write('└────────────────────────────────────┘');
- GotoXY(5,23);
- Write('Tail-Pointer');
- TextColor(Black);
- TextBackground(White);
- GotoXY(1,25);
- Write(' Ctrl-S Ausgabe unterdrücken / Ctrl-Q Ausgabe zulassen / ESC Programmende ');
- TextColor(White);
- TextBackground(Black);
- ReleaseCPU;
- WriteCharXY(25,11,#30);
- WriteCharXY(35,11,#30);
- WriteCharXY(42,11,#30);
- WriteCharXY(51,11,#30);
- WriteCharXY(25,17,#30);
- WriteCharXY(35,17,#30);
- WriteCharXY(42,17,#30);
- WriteCharXY(51,17,#30);
- WriteCharXY(21,19,#25);
- WriteCharXY(21,23,#24);
- END; {DrawScreen}
-
- {-----------------------------------------------------------------------------}
-
- FUNCTION InitConPro:BOOLEAN;
-
- { Initialisiere die Datenstrukturen und erzeuge die Tasks.
- Tritt hierbei ein Fehler auf, so wird der Funtionswert False geliefert. }
-
- BEGIN {InitConPro}
- InitConPro := False;
- WITH RBuff DO
- BEGIN
- FillChar(Buff,RBuffSize,' '); {Ausblanken des Buffers}
- Head := 0;
- Tail := 0;
- IF CreateSem(Critical) <> Sem_OK {Anlegen der Semaphoren}
- THEN Exit;
- IF CreateSem(Full) <> Sem_OK
- THEN Exit;
- IF CreateSem(Empty) <> Sem_OK
- THEN Exit;
- SemSet(Empty,RBuffSize); {alle Slots sind leer und dem-}
- SemClear(Full); {entsprechend keiner voll}
- END;
- IF CreateSem(ProgramEnd) <> Sem_Ok {Programmende-Flag erzeugen}
- THEN Exit;
- SemClear(ProgramEnd); {und zurücksetzen}
- IF CreateSem(OutputSem) <> Sem_Ok {Semaphore für Ausgabesteuerung}
- THEN Exit;
-
- ConsumerNo := CreateTask(Consumer,NIL,Pri_Kernel,500); {Erzeuge Consumer-Task}
- ProducerNo := CreateTask(Producer,NIL,Pri_User,500); {Erzeuge Producer-Task}
- IF (ConsumerNo < 0) OR
- (ProducerNo < 0)
- THEN Exit;
- DrawScreen;
- InitConPro := True;
- END; {InitConPro}
-
- {-----------------------------------------------------------------------------}
-
- BEGIN {Main}
- IF NOT InitConPro
- THEN BEGIN
- Writeln('Fehler bei der Initialisierung!');
- Halt;
- END;
- { Das Hauptprogramm bleibt an dieser Stelle durch den SemWait "hängen",
- bis ein SemSignal auf die Semaphore ProgramEnd abgesetzt wird }
- SW(ProgramEnd,PendCol,PendLin);
- END. {Main}