10. Parallele Prozesse
 
zurück
10.7.6 On Requeue-Anweisung


Jeder Eingang eines Prozesses besitzt eine Warteschlange ("queue"), in der die Eingangsaufrufe nacheinander in der Reihenfolge ihres Eintreffens aufgereiht sind. Eine Warteschlange an einem Eingang ist nach dem "FIFO"-Prinzip ("first in, first out") aufgebaut. Akzeptiert der Prozeß einen Ruf, so wird für den ersten - er ist am längsten in der Warteschlange - das Rendezvous eingeleitet. Stellt ein Prozeß fest, daß der von ihm akzeptierte Eingangsaufruf, nachdem er ihn analysiert hat, an ihn eine Anforderung stellt, die er momentan nicht erfüllen kann, so kann der Prozeß diesen Eingangsaufruf an denselben oder einen anderen Eingang weiterleiten. Er kann dies mit der Requeue-Anweisung tun, obwohl das Rendezvous schon begonnen hat.

Folgende zwei Einschränkungen sind zu beachten:
  • Der Eingang muß genau die gleiche Parametrierung besitzen oder
  • der Eingang darf überhaupt keine Parameter besitzen.
Leitet der Prozeß einen Eingangsaufruf an denselben Eingang weiter, so ist das trivialerweise möglich. Der Eingangsaufruf wird einfach an die Warteschlange angehängt und wird zu einem späteren Zeitpunkt bearbeitet. Das Rendezvous ist damit beendet.

Die Requeue-Anweisung kann mit dem Zusatz "with abort" versehen werden. Dann wird eine Bedingung, die an dem Eingangsaufruf haftet (z. B. eine Zeitschranke, siehe zeitlich befristeter Eingangsaufruf) "weitergereicht". Bei einer Zeitschranke wird das ursprüngliche Verfallsdatum übernommen. Fehlt der Zusatz "with abort", so gelten die Bedingungen nicht mehr. Damit wird die Anforderung des rufenden Prozesses unterlaufen. Es ist wichtig, dies bei Verwendung der Requeue-Anweisung zu berücksichtigen. Da ein aufrufender Prozeß, der z. B. nur 0,2 Sekunden auf ein Rendezvous warten will oder gar nur ein Rendezvous akzeptiert, wenn es sofort stattfinden kann, durch die Requeue-Anweisung gezwungen wird, über seine Vorgaben hinaus zu warten.
Ein Prozeß, der intern mit der Requeue-Anweisung ohne den Zusatz "with abort" operiert, muß das in seiner Spezifikation unbedingt kommentieren.


Beispiel 1:

loop
   select
      accept a_input( s : in string ) do
         -- requeue b_input; -- (A)
         requeue b_input with abort; -- (B)
      end a_input;
   or
      accept b_input( s : in string ) do
          -- irgendwelche Anweisungen.
          -- ...
      end b_input;
   end select;
end loop;


Anweisung (A) würde die Zeichenkette "s" an den Eingang "b_input" weiterreichen. Anweisung (B) reicht außerdem eventuelle Bedingungen des Eingangsaufrufes weiter.


Beispiel 2:

Es gebe eine variable Anzahl von Kanälen zur Übertragung von Nachrichten, die sich in ihrer Bandbreite (d. h. Anzahl Zeichen pro Sekunde) unterscheiden. Über diese Kanäle tauschen verschiedene Prozesse textuelle und prioritär gestaffelte Nachrichten aus. Wichtige Nachrichten sind möglichst schnell zu senden. Zur Vereinfachung soll gelten, daß Sender und Empfänger in der Nachricht kodiert sind und jeder potentielle Empfänger alle Kanälen abhorcht, ob eine Nachricht für ihn gesendet wird. Die Kanäle sind nicht ausfallsicher. Die Nachrichten sind nicht länger als 1024 Zeichen.

Lösung: Es soll einen Prozeß geben, der einen Eingang mit zwei Parametern aufweist. Der erste Parameter nimmt die Priorität an und der zweite Parameter die Nachricht. Je wichtiger eine Nachricht ist, um so eher wird sie über einen Kanal mit großer Bandbreite gesendet. Fallen Kanäle aus, so werden unwichtige Nachrichten zugunsten wichtigerer Nachrichten zurückgestellt. Der Prozeß wird so entworfen, daß nicht der Text selber zwischengespeichert wird, sondern die Aufforderung zum Senden. Dies hat den Vorteil, daß der Prozeß, der eine Nachricht senden will, Kenntnis erlangt, ob und wann seine Nachricht gesendet wurde. Außerdem entfällt dadurch die Notwendigkeit, daß Speicherplatz (dynamisch) bereitgestellt werden muß.

-- Spezifikation
package manager is
   -- Abhaengig von der Applikation.
   -- Wird (normalerweise) in einem spezifischen Paket deklariert.
   type prioritaet is new integer range 1 .. 50;

   task type nachrichten_manager is
      entry start;
      entry ende;
      entry input( p : in prioritaet; s : in string );
   private
      entry puffer( p : in prioritaet; s : in string );
   end nachrichten_manager;
end manager;

-- Rumpf
with text_io;
package body manager is
   task body nachrichten_manager is
      neue_nachricht : boolean := false;
      neue_prioritaet : prioritaet := prioritaet'first;
      max_bandbreite : prioritaet := prioritaet'first;
      text : string( 1 .. 1024 );
      text_length : positive := 1;
      -- Vorbesetzung, damit Nachrichten gepuffert werden.
      kanal_frei : prioritaet := 10;
      -- Diese Variable wird auf 'true' gesetzt, falls dieser
      -- Prozess sich beenden soll.
      ende_var : boolean := false;

      procedure jetzt_beenden is -- Soll der Prozess beendet werden, so
      -- werden vorher auch die gepufferten
      -- Nachrichten gesendet.
      begin
         kanal_frei := prioritaet'first;
         ende_var := true;
      end jetzt_beenden;

      function beenden return boolean is
      begin
         return ende_var;
      end beenden;

      function ueberpruefe_kanaele return prioritaet is
      begin
         return kanal_frei;
      end ueberpruefe_kanaele;

   begin
      accept start; -- Der Prozess haelt hier, bis zum Startsignal.

      endlos_schleife:
      loop
         neue_nachricht := false;
         -- Ueberpruefung der Kanaele.
         -- In Abhaengigkeit von der zur Verfuegung stehenden
         -- Bandbreite der offenden Kanaele wird 'max_bandbreite'gesetzt.
         max_bandbreite := ueberpruefe_kanaele;

         select
            accept input( p : in prioritaet; s : in string ) do
               if p < max_bandbreite then    -- Prioritaet zu niedrig,
                  requeue puffer with abort; -- so wird gepuffert.
               end if;
               neue_prioritaet := p;
               neue_nachricht := true;
               text_length := s'last - s'first + 1;
               text( 1 .. text_length ) := s;
            end input;
         else
            null;
         end select;

         if not neue_nachricht then
            -- Ist keine neu angekommene Nachricht zu senden, so
            -- wird der Puffer (zyklisch) abgefragt.
            select
               accept puffer( p : in prioritaet; s : in string ) do
                  if p < max_bandbreite then
                     requeue puffer with abort;
                  end if;
                  neue_prioritaet := p;
                  neue_nachricht := true;
                  text_length := s'last - s'first + 1;
                  text( 1 .. text_length ) := s;
               end puffer;
            else
               if beenden then
                  exit endlos_schleife;
               end if;
            end select;
         end if;

         if not neue_nachricht then
            -- Solange Nachrichten zu senden sind, wird eine
            -- Aufforderung zur Beendigung nicht akzeptiert.
            select
               accept ende do
                  jetzt_beenden;
               end ende;
            else
               null;
            end select;
            -- Der Nachrichtenmanager wartet auf neue Nachrichten.
            -- (Damit wird dem Zuteilungsverfahren, die Moeglich-
            -- keit gegeben, anderen Prozessen die Prozessor-
            -- kapazitaet zuzuweisen.
            delay 1.0;
         else
            -- Prioritaetsabhaengiges Senden auf Kanal X.
            -- Abhaengig von 'neue_prioritaet'.
            text_io.put_line( "Sende " & text( 1 .. text_length ) );
         end if;
      end loop endlos_schleife;
   end nachrichten_manager;
end manager;



Als erstes wird geprüft, welche Kanäle frei sind. Davon abhängig wird die Schranke ("max_bandbreite") gesetzt, die entscheidet, welche Nachricht gesendet wird. Dann wird nachgesehen, ob eine neue Nachricht anliegt. Jede Nachricht ist priorisiert. Ist die Priorität gering, so wird die Nachricht mit Priorität im Eingang "puffer" zwischengespeichert. Ist die Priorität hoch, so wird die Nachricht nicht zwischengespeichert, sondern sofort weiterverarbeitet. Nach dem ersten "select" wird gefragt, ob eine neue Nachricht angenommen wurde, wenn nicht, so wird im zweiten "select" geprüft, ob irdendeine alte Nachricht gesendet werden muß. Die alte Nachricht wird mit ihrer Priorität der Warteschlange von "puffer" entnommen. Ist die Priorität gering, so wird sie sofort an die Warteschlange wieder angehängt. Die letzte If-Anweisung prüft, ob eine Nachricht zu senden ist; wenn nicht, so wird 0,7 Sekunden gewartet, wenn ja, so wird sie gesendet.


 
zurück
 Index   Ada Tour - Dokumentation  
© 2003 Förderverein Ada Deutschland e.V.