home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / multtsk / cpmult / demo / pro_con.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-04-07  |  14.8 KB  |  416 lines

  1. {$R-,S-,I-,D-,F-,V-,B-,N-,L-}
  2. {$M 16384,0,655360 }
  3. PROGRAM ProducerConsumer;
  4.  
  5. { Darstellung des Producer-Consumer-Problems am Beispiel einer
  6.   Tastaturein-/ausgabesteuerung.
  7.  
  8.   Funktionsbeschreibung:
  9.     Eingabe von Zeichen über die Tastatur und Ausgabe auf dem Bildschirm.
  10.     ESC bewirkt ein Beenden des Programmes.
  11.     ^S  suspendiert die Ausgabe bis zum Erhalt von ^Q; in der Zwischenzeit
  12.         eingegebenene Zeichen werden im Ringpuffer abgelegt, jedoch noch
  13.         nicht angezeigt.
  14.     ^Q  reaktiviert die Ausgabe; der gespeicherte Pufferinhalt wird augen-
  15.         blicklich dargestellt.
  16.  
  17.  
  18.   Stand: 01/89
  19.   Autor: Christian Philipps Software-Technik
  20.          Düsseldorfer Str. 316
  21.          4130 Moers 1
  22. }
  23.  
  24. USES Crt, CpMulti;
  25.  
  26. CONST RBuffSize = 36;                            {Größe des Ringpuffers}
  27.       CritLin   = 15;
  28.       CritCol   = 51;
  29.       FullLin   = 15;
  30.       FullCol   = 34;
  31.       EmptyLin  = 15;
  32.       EmptyCol  = 42;
  33.       PEndLin   = 15;
  34.       PEndCol   = 12;
  35.       OutLin    = 15;
  36.       OutCol    = 24;
  37.  
  38. VAR   RBuff     : RECORD
  39.                     Buff     : ARRAY[0..RBuffSize] OF CHAR;
  40.                                                  {Ringpuffer; 1 Element ver-
  41.                                                   schenkt, dadurch leichtere
  42.                                                   Handhabung.}
  43.                     Critical : Pointer;          {Semaphore für Synchronisa-
  44.                                                   tion des Zugriffs}
  45.                     Full     : Pointer;          {Semaphore zur Zählung beleg-
  46.                                                   ter Pufferplätze}
  47.                     Empty    : Pointer;          {Semaphore zur Zählung freier
  48.                                                   Pufferplätze}
  49.                     Head     : Byte;             {Head- und Tailpointer}
  50.                     Tail     : Byte;
  51.                   END;
  52.       OutputSem : Pointer;                       {Semaphore zur Ausgabesteuerung}
  53.       ProgramEnd: Pointer;                       {Signalisiere Programmende}
  54.       ConsumerNo,                                {Task-IDs}
  55.       ProducerNo: TaskNoType;
  56.  
  57. {-----------------------------------------------------------------------------}
  58.  
  59. FUNCTION NextSlot(S:Byte):Byte;
  60.  
  61. { Errechne nächste Rinpufferposition, die auf S folgt }
  62.  
  63. BEGIN {NextSlot}
  64.   NextSlot := Succ(S) MOD RBuffSize;
  65. END;  {NextSlot}
  66.  
  67. {-----------------------------------------------------------------------------}
  68.  
  69. PROCEDURE WriteCharXY(X,Y:Byte; C:Char);
  70.  
  71. { Ausgabe eines Zeichens an der Position X,Y. Hierbei wird durch entsprechende
  72.   CPU-Blockierung sichergestellt, daß niemals die Sequenz GotoXY und Write
  73.   durch einen Task-Weschesl unterbrochen werden kann! }
  74.  
  75. BEGIN {WriteCharXY}
  76.   BindCPU;
  77.   GotoXY(X,Y);
  78.   Write(C);
  79.   ReleaseCPU;
  80. END;  {WriteCharXY}
  81.  
  82. {-----------------------------------------------------------------------------}
  83.  
  84. PROCEDURE WriteByteXY(X,Y,B:Byte);
  85.  
  86. { Ausgabe eines Byte-Wertes an der Position X,Y 2-stellig.
  87.   Erläuterung siehe WriteCharXY }
  88.  
  89. BEGIN {WriteByteXY}
  90.   BindCPU;
  91.   GotoXY(X,Y);
  92.   Write(B:2);
  93.   ReleaseCPU;
  94. END;  {WriteByteXY}
  95.  
  96. {-----------------------------------------------------------------------------}
  97.  
  98. PROCEDURE Status;
  99.  
  100. { Einblenden des Task-Status }
  101.  
  102. BEGIN {Status}
  103.   BindCPU;
  104.   GotoXY(65,9);
  105.   Write(StateText[GetTaskState(ConsumerNo)]:10);
  106.   GotoXY(65,22);
  107.   Write(StateText[GetTaskState(ProducerNo)]:10);
  108.   ReleaseCPU;
  109. END;  {Status}
  110.  
  111. {-----------------------------------------------------------------------------}
  112.  
  113. PROCEDURE SW(S:Pointer; c,l:byte);
  114.  
  115. { Aufruf SemWait mit Visualisierung }
  116.  
  117. BEGIN {SW}
  118.   WriteByteXY(C,L,SemGetSignals(S));
  119.   SemWait(S);
  120.   WriteByteXY(C,L,SemGetSignals(S));
  121.   Status;
  122. END;  {SW}
  123.  
  124. {-----------------------------------------------------------------------------}
  125.  
  126. PROCEDURE SS(S:Pointer; c,l:byte);
  127.  
  128. { Aufruf SemSignal mit Visualisierung }
  129.  
  130. BEGIN {SS}
  131.   SemSignal(S);
  132.   WriteByteXY(C,L,SemGetSignals(S));
  133.   Status;
  134. END;  {SS}
  135.  
  136. {-----------------------------------------------------------------------------}
  137.  
  138. FUNCTION RBuffPut(C:Char):BOOLEAN;
  139.  
  140. { Stelle ein Zeichen im Ringpuffer ab. True, falls erfolgreich; False,
  141.   wenn Pufferüberlauf. Da die Eingabesteuerung das Verhalten der Ausgabe-
  142.   Task beeinflußt (^S und ^Q), darf bei der Einspeisung von Zeichen in den
  143.   Ringpuffer niemals die Eingabesteuerung für längere Zeit blockiert werden.
  144.   Letzteres wäre aber der Fall, wenn in jedem Fall auf das Freiwerden eines
  145.   Pufferslots gewartet würde. Ist nämlich gerade die Ausgabesteuerung nach
  146.   Empfang eines ^S nicht in der Lage, einen Slot zu leeren und die Eingabe-
  147.   steuerung blockiert beim Wait auf einen Speicherplatz, so hängt das Ge-
  148.   samtsystem unwiederruflich!!
  149.   ACHTUNG!! Man beachte die Positionierung der Wait-Aufrufe auf die Synchro-
  150.   nisations-Semaphore Critical!! Da eine Blockierung durch Wait auf Empty
  151.   abhängig vom Signal-Count der Empty-Semaphore ist, jedoch THEORETISCH
  152.   in der Zeit zwischen der Abfrage des Signal-Count und dem Wait auf Empty
  153.   eine andere Task den letzten Slot des Puffer belegt haben könnte,
  154.   muß durch eine entsprechende Abgrenzung des kritischen Bereiches sicher-
  155.   gestellt sein, daß bis zur endgültigen Belegung des Pufferplatzes, keine
  156.   andere Task Zugang zum Ringpuffer erhalten kann.}
  157.  
  158. BEGIN {RBuffPut}
  159.   WITH RBuff DO
  160.   BEGIN
  161.     SW(Critical,CritCol,CritLin);                {exclusiver Pufferzugriff}
  162.     IF SemGetSignals(Empty) = 0                  {Puffer voll}
  163.        THEN RBuffPut := False                    {Verhindere Blockierung}
  164.        ELSE BEGIN
  165.               RBuffPut := True;
  166.               SW(Empty,EmptyCol,EmptyLin);       {belege einen Slot}
  167.               Buff[Tail] := c;                   {stelle Zeichen ab}
  168.               WriteCharXY(21+Tail,19,' ');
  169.               IF C = #13
  170.                  THEN WriteCharXY(21+Tail,21,#188)
  171.                  ELSE WriteCharXY(21+Tail,21,c);
  172.               Tail := NextSlot(Tail);            {Headpointer auf nächsten Slot}
  173.               WriteCharXY(21+Tail,19,#25);
  174.               SS(Full,FullCol,FullLin);          {1 Zeichen mehr im Puffer}
  175.             END;
  176.     SS(Critical,CritCol,CritLin);                {Puffer freigeben}
  177.   END;
  178. END;  {RBuffPut}
  179.  
  180. {-----------------------------------------------------------------------------}
  181.  
  182. FUNCTION RBuffGet:Char;
  183.  
  184. { Entnimm das erste Zeichen aus dem Ringpuffer und stelle es der Anwendung
  185.   zur Verfügung. Ist der Puffer gerade leer, so warte auf das Eintreffen
  186.   eines Zeichens }
  187.  
  188. BEGIN {RBuffGet}
  189.   WITH RBuff DO
  190.   BEGIN
  191.     SW(Full,FullCol,FullLin);                    {Fordere Zeichen an}
  192.     SW(Critical,CritCol,CritLin);                {exclusiver Zugriff}
  193.     RBuffGet := Buff[Head];                      {entnimm Zeichen}
  194.     WriteCharXY(21+Head,23,' ');
  195.     Head := NextSlot(Head);                      {aktualisiere Head-Pointer}
  196.     WriteCharXY(21+Head,23,#24);
  197.     SS(Critical,CritCol,CritLin);                {Freigeben des Puffers}
  198.     SS(Empty,EmptyCol,EmptyLin);                 {1 Slot mehr frei}
  199.   END;
  200. END;  {RBuffGet}
  201.  
  202. {-----------------------------------------------------------------------------}
  203.  
  204. {$F+}
  205. PROCEDURE Producer(P:Pointer);
  206.  
  207. { Eingabesteuerung: Lies alle verfügbaren Zeichen von der Tastatur in den
  208.   Ringpuffer ein. Wird ein ^S empfangen, so wird die Ausgabe von Zeichen
  209.   bis zum Erhalt eines ^Q unterbunden }
  210.  
  211. VAR   C       : Char;
  212.       Display : Boolean;
  213.       Col     : Byte;
  214.  
  215. BEGIN {Producer}
  216.   Display := True;                               {Anzeige erlaubt}
  217.   Col := 1;
  218.   REPEAT                                         {Endlosschleife}
  219.     WHILE Keypressed DO
  220.     BEGIN
  221.       C := ReadKey;
  222.       CASE C OF
  223.         ^S: IF Display                           {sofern nicht bereis passiert}
  224.                THEN BEGIN
  225.                       SW(OutputSem,OutCol,OutLin); {Ausgabe unterbinden}
  226.                       Display := False;          {Zustand merken}
  227.                     END;
  228.         ^Q: IF NOT Display                       {falls Ausgabe unterbunden}
  229.                THEN BEGIN
  230.                       SS(OutputSem,OutCol,OutLin); {Ausgabe freigeben}
  231.                       Display := True;           {neuen Status merken}
  232.                     END;
  233.        ELSE                                      {kein Sonderzeichen}
  234.          BEGIN
  235.            IF NOT RBuffPut(C)
  236.               THEN BEGIN                         {Überlauf}
  237.                      BindCPU;                    {atomic action}
  238.                      GotoXY(34,18);
  239.                      TextBackground(White);
  240.                      TextColor(Black);
  241.                      Write(' Überlauf ');
  242.                      TextColor(White);
  243.                      TextBackground(Black);
  244.                      ReleaseCPU;                 {Ende atomic action}
  245.                    END;
  246.          END;
  247.       END; {Case}
  248.     END;
  249.     Sched;                                       {alle Zeichen verarbeitet,
  250.                                                   gib Zeitscheibe auf}
  251.   UNTIL False;
  252. END;  {Producer}
  253.  
  254.  
  255. {-----------------------------------------------------------------------------}
  256.  
  257. PROCEDURE Consumer(P:Pointer);
  258.  
  259. { Dies ist die Task, die ständig Zeichen aus dem Ringpuffer entnimmt und
  260.   auf dem Bildschirm ausgibt.
  261.   Ist ein ^S von der Eingabesteuerung empfangen worden, so belegt diese die
  262.   OutputSem-aphore und sorgt so für eine Blockierung der Ausgabe-Task beim
  263.   nächsten Versuch ein Zeichen auf dem Bildschirm auszugeben.
  264.   Bei Erhalt eines ^Q wir diese Semaphore wieder freigegeben.
  265.   Wird aus dem Puffer das Zeichen ESC entnommen, so veranlaßt diese Task das
  266.   Beenden des Programmes durch Incrementieren des Signal-Count der Semaphore
  267.   ProgramEnd.
  268.   Die Consumer-Task läuft mit höchster Priorität, da sie die meiste Zeit
  269.   ohnehin auf Eingaben wartet und daher andere Tasks nicht behindert. Lie-
  270.   gen jedoch Zeichen an, so werden diese im Interesse einer schnellen Re-
  271.   aktion augenblicklich bearbeitet. }
  272.  
  273. CONST  MaxCols = 50;
  274.  
  275. VAR  C   : Char;
  276.      Col : Byte;
  277.  
  278. BEGIN {Consumer}
  279.   Col := 1;
  280.   REPEAT                                         {Endlosschleife}
  281.     C := RBuffGet;                               {hole Zeichen}
  282.     GotoXY(34,18);                               {lösche Überlauftext}
  283.     Write('          ');
  284.     IF C = #27
  285.        THEN SS(ProgramEnd,PendCol,PendLin)       {Ende des Programmes}
  286.        ELSE BEGIN
  287.               SW(OutPutSem,OutCol,OutLin);       {warte auf Ausgabeerlaubnis}
  288.               IF (Col >= MaxCols) OR (C=#13)     {Display-Überlauf / Return}
  289.                  THEN BEGIN
  290.                         BindCPU;                 {kritischer Bereich}
  291.                         GotoXY(7,8);
  292.                         FOR Col := 1 TO MaxCols DO
  293.                           Write(' ');
  294.                         ReleaseCPU;              {Ende des kritischen Bereiches}
  295.                         Col := 1;
  296.                       END;
  297.               IF C <> #13                        {Zeichenausgabe}
  298.                  THEN BEGIN
  299.                         WriteCharXY(6+Col,8,C);
  300.                         Inc(Col);
  301.                       END;
  302.               SS(OutPutSem,OutCol,OutLin);       {Sempahore wieder setzen}
  303.             END;
  304.   UNTIL False;
  305. END;  {Consumer}
  306. {$F-}
  307.  
  308. {-----------------------------------------------------------------------------}
  309.  
  310. PROCEDURE DrawScreen;
  311.  
  312. BEGIN {DrawScreen}
  313.   ClrScr;
  314.   BindCPU;
  315.   GotoXY(15,1);
  316.   Write('P R O Z E S S  -  S Y N C H R O N I S A T I O N');
  317.   GotoXY(18,3);
  318.   Write('Darstellung des Producer-Consumer Problems');
  319.   GotoXY(24,4);
  320.   Write('Autor: Christian Philipps 6/88');
  321.   GotoXY(5,7);
  322.   Write('┌───────────────────────────────────────────────────┐');
  323.   GotoXY(5,8);
  324.   Write('│                                                   │ Consumer-Task');
  325.   GotoXY(5,9);
  326.   Write('└───────────────────────────────────────────────────┘');
  327.   GotoXY(6,12);
  328.   Write('┌────────────┬───────────┬──────┬───────┬──────────┐');
  329.   GotoXY(6,13);
  330.   Write('│ ProgramEnd │ OutputSem │ Full │ Empty │ Critical │ Semaphoren zur');
  331.   GotoXY(6,14);
  332.   Write('├────────────┼───────────┼──────┼───────┼──────────┤ Prozeß- und Zugiffs-');
  333.   GotoXY(6,15);
  334.   Write('│            │           │      │       │          │ synchronisation');
  335.   GotoXY(6,16);
  336.   Write('└────────────┴───────────┴──────┴───────┴──────────┘');
  337.   GotoXY(5,19);
  338.   Write('Head-Pointer');
  339.   GotoXY(20,20);
  340.   Write('┌────────────────────────────────────┐');
  341.   GotoXY(5,21);
  342.   Write('Ringpuffer ->  │                                    │ Producer─Task');
  343.   GotoXY(20,22);
  344.   Write('└────────────────────────────────────┘');
  345.   GotoXY(5,23);
  346.   Write('Tail-Pointer');
  347.   TextColor(Black);
  348.   TextBackground(White);
  349.   GotoXY(1,25);
  350.   Write('  Ctrl-S  Ausgabe unterdrücken / Ctrl-Q  Ausgabe zulassen / ESC  Programmende  ');
  351.   TextColor(White);
  352.   TextBackground(Black);
  353.   ReleaseCPU;
  354.   WriteCharXY(25,11,#30);
  355.   WriteCharXY(35,11,#30);
  356.   WriteCharXY(42,11,#30);
  357.   WriteCharXY(51,11,#30);
  358.   WriteCharXY(25,17,#30);
  359.   WriteCharXY(35,17,#30);
  360.   WriteCharXY(42,17,#30);
  361.   WriteCharXY(51,17,#30);
  362.   WriteCharXY(21,19,#25);
  363.   WriteCharXY(21,23,#24);
  364. END;  {DrawScreen}
  365.  
  366. {-----------------------------------------------------------------------------}
  367.  
  368. FUNCTION InitConPro:BOOLEAN;
  369.  
  370. { Initialisiere die Datenstrukturen und erzeuge die Tasks.
  371.   Tritt hierbei ein Fehler auf, so wird der Funtionswert False geliefert. }
  372.  
  373. BEGIN {InitConPro}
  374.   InitConPro := False;
  375.   WITH RBuff DO
  376.   BEGIN
  377.     FillChar(Buff,RBuffSize,' ');                {Ausblanken des Buffers}
  378.     Head := 0;
  379.     Tail := 0;
  380.     IF CreateSem(Critical) <> Sem_OK             {Anlegen der Semaphoren}
  381.        THEN Exit;
  382.     IF CreateSem(Full) <> Sem_OK
  383.        THEN Exit;
  384.     IF CreateSem(Empty) <> Sem_OK
  385.        THEN Exit;
  386.     SemSet(Empty,RBuffSize);                     {alle Slots sind leer und dem-}
  387.     SemClear(Full);                              {entsprechend keiner voll}
  388.   END;
  389.   IF CreateSem(ProgramEnd) <> Sem_Ok             {Programmende-Flag erzeugen}
  390.      THEN Exit;
  391.   SemClear(ProgramEnd);                          {und zurücksetzen}
  392.   IF CreateSem(OutputSem) <> Sem_Ok              {Semaphore für Ausgabesteuerung}
  393.      THEN Exit;
  394.  
  395.   ConsumerNo := CreateTask(Consumer,NIL,Pri_Kernel,500); {Erzeuge Consumer-Task}
  396.   ProducerNo := CreateTask(Producer,NIL,Pri_User,500);   {Erzeuge Producer-Task}
  397.   IF (ConsumerNo < 0) OR
  398.      (ProducerNo < 0)
  399.      THEN Exit;
  400.   DrawScreen;
  401.   InitConPro := True;
  402. END;  {InitConPro}
  403.  
  404. {-----------------------------------------------------------------------------}
  405.  
  406. BEGIN {Main}
  407.   IF NOT InitConPro
  408.      THEN BEGIN
  409.             Writeln('Fehler bei der Initialisierung!');
  410.             Halt;
  411.           END;
  412.   { Das Hauptprogramm bleibt an dieser Stelle durch den SemWait "hängen",
  413.     bis ein SemSignal auf die Semaphore ProgramEnd abgesetzt wird }
  414.   SW(ProgramEnd,PendCol,PendLin);
  415. END.  {Main}
  416.