Lua Logo
Coroutinen

Den Coroutinen widmen wir hier ein extra Kapitel, da alles zusammen gehört.

Eine Coroutine ist eine besondere Art von Funktion. Man kann jede Funktion in eine Coroutine Umwandeln. Sehr wichtig um zu verstehen sind die Beispiele.
Wir sind es gewohnt, dass eine Funktion eins nach dem anderen abarbeitet bis das Ende erreicht ist.
Das macht eine Coroutine auch, nur mit einem gewaltigen Unterschied.
Man kann sie anhalten und nach Belieben irgendwann weiter laufen lassen. Alle Variablen, die bis dahin erarbeitet wurden, bleiben erhalten. Die Funktion macht an dieser Stelle weiter, als wenn nichts gewesen wäre. (Wenn sie wieder angestoßen wird)

Beispiele kommen zum Schluss, da man dies nur im Zusammenhang begreifen kann.
Die Funktionen für Coroutinen sind in dem Table

coroutine

gespeichert.

coroutine.create (f)
coroutine.resume (co, val1, ...)
coroutine.status (co)
coroutine.wrap (f)
coroutine.yield (val1, ...)

Coroutine - Beispiel 1
Coroutine - Beispiel 2
Coroutine - Beispiel 3
Coroutine - Beispiel 4





nach obencoroutine.create

Diese Funktion erzeugt eine Coroutine.

Als Parameter ist folgendes zu übergeben:
  • Die Funktion, die als Coroutine arbeiten soll.
Rückgabe ist eine Variable vom Typ "thread" (ein eigener Typ um diese Coroutines durchzuführen)

meineRoutine = coroutine.create(DruckeEtwas) -- Die Coroutine
-- wird erzeugt, aber sie macht noch nichts. Sie muß erst angestoßen werden


nach obencoroutine.resume

Diese Funktion lässt eine Coroutine laufen. (Stößt sie an)

Als Parameter ist folgendes zu übergeben:
  • Die Variable, die bei der Erschaffung der Coroutine zurückgegeben wurde.

Die Parameter werden nur beim ersten Aufruf der resume - Funktion übernommen. Bei jedem weiteren werden sie ignoriert.

Rückgabe ist zum einen der status der Funktion zum anderen die Parameter, die beim Anhalten der Funktion zurückgegeben werden

status, val1, ... = coroutine.resume( meineRoutine, 50)
-- Die Coroutine wird gestartet. Ein Parameter (hier 50) wird ihr übergeben.


Der Status ist true, wenn die Funktion ohne Error lief.
Val1, ... sind entweder die Werte, die beim Stoppen der Funktion übergeben werden, oder die Werte, die bei Beendigung der Funktion übergeben werden.



nach obencoroutine.status

Diese Funktion gibt den derzeitigen Status der Coroutine als String zurück.

Als Parameter ist folgendes zu übergeben:
Die Variable, die bei der Erschaffung der Coroutine zurückgegeben wurde.


result = coroutine.status(meineRoutine)

result kann drei Zustände haben.

  • "running" die Funktion läuft
  • "suspended" die Funktion ist beurlaubt
  • "dead" die Funktion ist beendet
"dead" tritt bei normaler Beendigung oder auch bei Beendigung durch einen Fehler auf.



nach obencoroutine.wrap

Diese Funktion erzeugt ebenfalls eine Coroutine. Allerdings kann bei dieser nicht der Staus abgefragt werden.

Als Parameter ist folgendes zu übergeben:

Rückgabe ist eine Variable vom Typ "function"

meineAufrufFunktion = coroutine.wrap( DruckeEtwas)
-- Die Coroutine wird erzeugt

meineAufrufFunktion gibt entweder die Werte, die beim Stoppen der Funktion übergeben werden, oder die Werte, die bei Beendigung der Funktion übergeben werden zurück. Als Parameter gibt man die Parameter, die man übergeben möchte.


Value = meineAufrufFunktion(50)






nach obencoroutine.yield

Diese Funktion stoppt eine Coroutine. Daher muss sie innerhalb der Funktion liegen.

Als Parameter ist folgendes zu übergeben:
  • Die Werte, die man resume oder der Aufruffunktion zurückgeben möchte.
Rückgabe keine.

coroutine.yield( 1000 )


Die Funktion (Coroutine) wird gestoppt und der Wert 1000 an die Aufruffunktion übergeben



nach obenCoroutine - Beispiel 1



function UeberwacheRoutine()
if value + beginnRoutine <= os.clock() *1000 then
coroutine.resume( meineRoutine)
-- hier wird die Coroutine zum zweiten Mal
-- gestartet. Es muss kein Parameter mehr da sein,
-- er würde so wie so ignoriert
return true
end
end

function DruckeEtwas(_wieviel)
print(_wieviel)
print ( "Status3 = "..coroutine.status(meineRoutine))
coroutine.yield( 1000 ) -- Stopp
_wieviel = _wieviel - 1
print(_wieviel)
end

beginnRoutine = os.clock() * 1000
-- Anfangszeit in 1000tel Sekunden
meineRoutine = coroutine.create( DruckeEtwas)
-- Die Coroutine wird erzeugt

print ( "Status1 = "..coroutine.status(meineRoutine))

status, value = coroutine.resume( meineRoutine, 50)
-- Die Coroutine wird zum ersten mal gestartet. ein Parameter
-- (hier 50 ) wird Ihr übergeben

print ( "Status2 = "..coroutine.status(meineRoutine))

while not UeberwacheRoutine() do end
-- solange die Funktion nicht true zurückgibt, wird sie immer
-- wieder ausgeführt

print ( "Status4 = "..coroutine.status(meineRoutine))



Ablauf Beispiel 1:


  • Der Computer liest der Reihe nach die beiden Funktionen ein.
  • Dann speichert er die Zeit in die Variable beginnRoutine
  • Die Coroutine wird erzeugt und der thread in die Variable meineRoutine gespeichert
  • Der derzeitige Status der Coroutine wird ausgedruckt. Da sie noch nicht läuft kommt suspended als Ergebnis
  • Wir starten die Routine und übergeben Ihr den Parameter 50
  • Die Routine druckt den Wert 50 aus.
  • Die Routine druckt ihren Status aus: running. (sie läuft ja)
  • Die Routine stoppt und übergibt an resume true und 1000
  • true wird in die Variable status gespeichert und die 1000 in die Variable value
  • Der derzeitige Status der Coroutine wird ausgedruckt. Da sie nicht mehr läuft kommt suspended als Ergebnis
  • Dann geht es in eine Schleife, die so lange die Funktion UeberwacheRoutine ausführt bis diese einen Wert der ungleich
    nil oder false ist zurück liefert
  • In der Funktion wird laufend geprüft ob die Beginnzeit um den Wert 1000 gestiegen ist. (1 Sekunde) Ist das der Fall,
    so geht es in den Block.
  • Die Coroutine wird dann im Block zum zweiten Mal angestoßen und macht hinter yield weiter
  • Dort wird von unserem Parameter 1 abgezogen und dann ausgedruckt.
  • Die Funktion endet.
  • Die Funktion UeberwacheRoutine endet damit, dass sie den Wert true zurückliefert.
  • Damit ist die Bedingung für die while - Schleife false und diese endet auch.
  • Der derzeitige Status der Coroutine wird ausgedruckt. Da sie beendet ist, kommt dead als Ergebnis.




nach obenCoroutine - Beispiel 2

function UeberwacheRoutine()
if value + beginnRoutine <= os.clock() *1000 then
beginnRoutine = os.clock() *1000
status, value = coroutine.resume( meineRoutine, 50)
end
end

function DruckeEtwas(_wieviel)
for i = 1, 10 do
print(_wieviel)
coroutine.yield( 1000 )
_wieviel = _wieviel - 1
end
return "ich habe fertig"
end

beginnRoutine = os.clock() *1000
meineRoutine = coroutine.create( DruckeEtwas)
status, value = coroutine.resume( meineRoutine, 50)
zeit = os.clock() *1000

while coroutine.status (meineRoutine) ~= "dead" do
UeberwacheRoutine()
end
print (value)

Wenn oben das verstanden ist, geht es hier etwas einfacher zu.

Bis auf die Schleife sieht es im ersten Augenblick ziemlich gleich aus.

  • Die Coroutine startet, läuft in die for - Schleife, druckt aus und stoppt.
  • Die 1000 werden wieder in value gespeichert. Die nächste Schleife läuft so lange, bis die Coroutine des Status "dead" hat,
    also beendet ist.
  • Ist die Sekunde um, so wird die neue Zeit in die Variable beginnRoutine gespeichert.
  • Die Routine wird wieder angestoßen.
  • Zieht von _wieviel 1 ab und druckt es aus.
  • Wieder Stopp. Rückgabe ist wieder 1000
  • Jetzt überwacht die erste Funktion, ob die nächste Sekunde verstrichen ist.
  • Das Ganze läuft weiter, bis die for - Schleife beendet ist
  • Dann druckt es noch "ich habe fertig" aus

nach obenCoroutine - Beispiel 3




function UeberwacheRoutine()
if value + beginnRoutine <= os.clock() *1000 then
meineAufrufFunktion()
-- hier wird die Coroutine zum zweiten Mal
-- gestartet. Es muss kein Parameter mehr da sein,
-- er würde sowieso ignoriert
return true
end
end

function DruckeEtwas( _wieviel )
print(_wieviel)
coroutine.yield( 700 )
_wieviel = _wieviel - 1
print(_wieviel)
end

beginnRoutine = os.clock() *1000
-- Anfangszeit in 1000tel Sekunden
meineAufrufFunktion = coroutine.wrap( DruckeEtwas )
-- Die Coroutine wird erzeugt

value = meineAufrufFunktion( 50 )
-- Die Coroutine wird zum ersten Mal gestartet. ein Parameter
-- (hier 50 ) wird Ihr übergeben

while not UeberwacheRoutine() do end
-- solange die Funktion nicht true zurückgibt wird sie immer
-- wieder ausgeführt


Ähnlich wie im Beispiel 1 die Unterschiede:

1. Einmal die Art des Aufrufs und dann,
2. dass wir keinen Status abfragen können.






nach obenCoroutine - Beispiel 4


function UeberwacheRoutine()
if value + beginnRoutine <= os.clock() *1000 then
beginnRoutine = os.clock() *1000
value = meineAufrufFunktion()
end
end

function DruckeEtwas(_wieviel)
for i = 1, 10 do
print(_wieviel)
coroutine.yield( 50 )
_wieviel = _wieviel - 1
end
return -1
end

beginnRoutine = os.clock() *1000
meineAufrufFunktion = coroutine.wrap( DruckeEtwas) -- Die Coroutine wird erzeugt und die resum-Funktion gespeichert

value = meineAufrufFunktion(50) -- Die Coroutine wird zum ersten Mal gestartet. Ein Parameter (hier 50) wird Ihr übergeben

while value ~= -1 do
UeberwacheRoutine()
end


Der Unterschied zum Beispiel 2: Man muss sich etwas einfallen lassen um herauszufinden ob die Funktion beendet ist. (hier gebe ich -1 zurück)

Abfragen kann man es ja nicht

nach obenEnde Coroutinen
Die Vervielfältigung der auf diesen Seiten enthaltenen Informationen und Grafiken ist untersagt, ausgenommen davon ist sämtlicher auf diesen Seiten angezeigter Quellcode.
Siehe auch: Haftungsausschluss
Copyright © Robert Schmitz 2006