Rendezvous mit Alternativen
Rendezvous mit Alternativen
Accept-Alternativen
<selective_accept> ::=
select
[when <condition> =>]
<selective_accept_alternative>
or
[when <condition> =>]
<selective_accept_alternative>
[else
<statement_sequence>]
end select;
<selective_accept_alternative> ::=
<accept_alternative>
| <delay_alternative>
| <terminate_alternative>
accept_alternative
mehrereerstenserver tasksverschiedene Dienste
<accept_alternative> ::=
<accept_statement> [<statement_sequence>]
task Server is
entry Service1(...);
...
entry ServiceN(...);
end Server;
task body Server is
...
begin
loop
select
accept Service1(...) do
...
end Service1;
- internal work 1 -
or
...
or
accept ServiceN(...) do
...
end ServiceN;
- internal work N -
end select;
end loop;
end Server;
Guards
select
when - Dienst möglich - =>
accept Service1(...) do
...
end Service1;
- interne Arbeit -
or
...
end select;
terminate Alternative
Terminierungda sind
<terminate_alternative> ::= terminate;
loop
select
accept Service1(...) do
...
end Service1;
or
...
or terminate;
end select;
end loop;
Methodische Bemerkung
Servern- protected object mit service = protected operation oder entry
- task mit Endlosschleife + select accept service
Variante 1 (= passives Objekt) ist oft besser (einfaches Programm, effiziente Realisierung), es sei denn der Server muß zwischen den Diensten lokal Arbeit leisten (aufräumen, nächsten Dienst vorbereiten, ...), wodurch der Klient nicht verzögert werden sollte.
Beispiel: server task
-----------------------------------
- Aufgabe: producer-consumer (summation_of_naturals) with bounded buffer
- Methode: tasks for producer-consumer (simple sum_of_naturals problem)
- AND task for buffer
-----------------------------------
with Stringpack; use Stringpack;
procedure Bounded_Buffer2 is
task Buffer is - server task
entry Insert(D: Natural);
entry Take(D: out Natural);
end Buffer;
task Producer is
entry Start(How_Many: Natural);
end Producer;
task Consumer is
entry Start(Break: Natural);
end Consumer;
task body Producer is
Local_How_Many: Natural;
begin
accept Start(How_Many: Natural) do
Local_How_Many := How_Many;
end Start;
Consumer.Start(Local_How_Many +1);
for I in 1..(Local_How_Many +1) loop
Buffer.Insert(I);
end loop;
end Producer;
task body Consumer is
Over, Item, Result: Natural;
begin
accept Start(Break: Natural) do
Over := Break;
end Start;
Result := 0;
Buffer.Take(Item);
while Item /= Over loop
Result := Result +Item;
Buffer.Take(Item);
end loop;
Print(SSumme= " & Result);
end Consumer;
task body Buffer is
Length: constant Natural := 10;
B: array(0..Length-1) of Natural;
In_Ptr, Out_Ptr: Natural := 0;
Count: Natural := 0;
begin
loop
select
when Count < Length =>
accept Insert(D: Natural) do
B(In_Ptr) := D;
end Insert;
In_Ptr := (In_Ptr +1) mod Length;
Count := Count +1;
or
when Count > 0 =>
accept Take(D: out Natural) do
D := B(Out_Ptr);
end Take;
Out_Ptr := (Out_Ptr +1) mod Length;
Count := Count -1;
or
terminate;
end select;
end loop;
end Buffer;
N: Natural;
begin
Print("give natural");
N := Getint;
Producer.Start(N);
end Bounded_Buffer2;
-----------------------------------
45 pohlmann@mandrill:bounded_buffer2
give natural
100
Summe= 5050
Beispiel: Primzahlen durch Siebmethode
unendlichen ListenProzessenächste Element- Passive Objekte mit internem Zustand + give_next-Operation.
- Aktive Objekte wie Ada task mit give_next-entry.
Hausaufgabe
- Die tasks, die die unendlichen Folgen repräsentieren, können nur einen Klienten bedienen (entry next = Einmal-Durchlauf). Überlegen Sie sich eine Realisierung, die beliebig viele Klienten bedienen kann (und dazu bereits erzeugte Folgenelemente jeweils speichert).
- In der Literatur (z.B. Barnes, Burns/Wellings) wird die Primzahl/Sieb-Aufgabe meist anders gelöst, nämlich mit umgekehrter Aufrufstruktur: Zahlenfolge stößt Nachfolger-Zahlenfolge an. Betrachten (realisieren) Sie eine solche Lösung und überlegen Sie Vor- und Nachteile!
Delay-Alternative
timeouts
<delay_alternative> ::=
<delay_statement> [<sequence_of_statements>]
<delay_statement> ::= delay <expr>;
| delay until <expr>;
else-Teil des select-statements
nicht sofort
... ...
select select
accept E; accept E;
or else
delay 0.0; Statement;
Statement; end select;
end select; ...
...
Man vermeide ,,polling`` d.h. ständiges Abfragen (busy waiting).
...
loop
select
accept E;
else
null;
end select;
end loop;
...
Sinnvolle Anwendungen von ,,else`` sind selten!
Entry-Call mit Alternative
Anders als bei selective-accept ist bei Aufrufen nur 1 Alternative möglich.
Nämlich:
Timed entry call
<timed_entry_call> ::=
select
<entry_call_alternative>
or
<delay_alternative>
end select;
<entry_call_alternative> ::=
<entry_call_statement>
[<statement_sequence>]
Also z.B.:
loop
select
Buffer.Take(X);
Consume(X);
or
delay 5.0;
raise Alarm; - exception
end select;
end loop;
Conditional entry call
<conditional_entry_call> ::=
select
<entry_call_alternative>
else
<statement_sequence>
end select;
Auswahl unter mehreren entry calls
nichtErsatzlösungen-
Umkehrung der Aufrufrichtung: call accept.
Oft unnatürlich! - Einführung von Hilfstask als Zwischenglied, kann Probleme bei 1 lösen.
- Wiederversuch (polling) mit ,,sinnvollem`` Abstand (ungleich 0, eventuell random).
z.B.:
task type Server is
entry Service;
end Server;
Server_List: array(1..N) of Server;
task Client;
task body Client is
begin
L: loop
for I in Server_List'Range loop
select
Server(I).Service;
exit L;
else
null;
end select;
end loop;
delay Some_Ticks;
end loop L;
end Client;
- Ähnlich: Mischung von entry-calls für tasks un protected objects.
- Klar: Ersatz ist nicht dasselbe wie ein ordentliches select:
Kleines Warteintervall $$ Ineffizient Großes Warteintervall $$ Langes Warten möglich (Prüfung selten)
Deadlock revisited
Verklemmung4
Nächste Seite:BeispieleAufwärts:Vorlesungsskriptum zu Spezielle AnwendersprachenVorherige Seite:Ada Tasking IIInhaltsverzeichnis Ronald Blaschke 2001-06-02
Dort: Gebrauch von gemeinsamen Variablen, z.B. 2 tasks haben jeweils sperrendes Signal gesetzt.
Hier: Verklemmungsgefahr bei Rendezvous.
-
task T1 is task T2 is
entry E1; entry E2;
end T1; end T2;
task body T1 is task body T2 is
begin begin
T2.E2; (*) (**) T1.E1;
accept E1; (**) (*) accept E2;
end T1; end T2;
-
... ...
task body T1 is task body T2 is
begin begin
accept E1; accept E2;
T2.E2; T1.E1;
end T1; end T2;
- 1
- Beide tasks unweigerlich verklemmt.
- 2
- Keine Verklemmung, wenn es dritte task gibt, die E1 oder E2 aufruft. Allerdings: Den letzten beißt der Hund ...
Wichtig im Kampf gegen deadlocks:
- Große Sorgfalt bei Entwurft/Verfeinerung von Algorithmen.
- Vorher Nachdenken (möglicherweise Beweise führen) anstatt auf Tests verlassen. Deadlock kann selten sein, kann durch anderweitige Systemaktivität verdeckt werden, und grundsätzlich: ,,ewige`` Inaktivität kann empirisch nicht von ,,langer`` aber endlicher Wartezeit unterschieden werden.
- Anwendung methodischer Regeln, falls für konkreten Fall passend, z.B.: Aufteilung in tasks, die ausschließlich entry-call-statements enthalten (,,clients``) und tasks, die ausschließlich entry-accept-statements enthalten (,,server``).
Nächste Seite:BeispieleAufwärts:Vorlesungsskriptum zu Spezielle AnwendersprachenVorherige Seite:Ada Tasking IIInhaltsverzeichnis Ronald Blaschke 2001-06-02