Recherche…
Syntaxe
coroutine.create (function) renvoie une coroutine (type (coroutine) == 'thread') contenant la fonction.
coroutine.resume (co, ...) reprend ou démarre la coroutine. Tous les arguments supplémentaires donnés pour reprendre sont renvoyés par le coroutine.yield () qui a précédemment suspendu la coroutine. Si la coroutine n'a pas été démarrée, les arguments supplémentaires deviennent les arguments de la fonction.
coroutine.yield (...) donne la coroutine en cours d'exécution. L'exécution reprend après l'appel à coroutine.resume () qui a démarré cette coroutine. Tous les arguments donnés pour céder sont renvoyés par la coroutine.resume () correspondante qui a démarré la coroutine.
coroutine.status (co) renvoie le statut de la coroutine, qui peut être:
- "mort": la fonction dans la coroutine a atteint sa fin et la coroutine ne peut plus être reprise
- "running": la coroutine a repris et est en cours d'exécution
- "normal": la coroutine a repris une autre coroutine
- "suspendu": la coroutine a cédé et attend d'être reprise
coroutine.wrap (function) renvoie une fonction qui, lorsqu'elle est appelée, reprend la coroutine qui aurait été créée par coroutine.create (function).
Remarques
Le système de coroutine a été implémenté en lua pour émuler le multithreading existant dans d'autres langages. Cela fonctionne en commutant à une vitesse extrêmement élevée entre les différentes fonctions pour que l'utilisateur humain pense qu'elles sont exécutées en même temps.
Créer et utiliser une coroutine
Toutes les fonctions d'interaction avec les coroutines sont disponibles dans la table de coroutine . Une nouvelle coroutine est créée en utilisant la fonction coroutine.create avec un seul argument: une fonction avec le code à exécuter:
thread1 = coroutine.create(function()
print("honk")
end)
print(thread1)
-->> thread: 6b028b8c
Un objet coroutine renvoie une valeur de type thread , représentant une nouvelle coroutine. Lorsqu'une nouvelle coroutine est créée, son état initial est suspendu:
print(coroutine.status(thread1))
-->> suspended
Pour reprendre ou démarrer une coroutine, la fonction coroutine.resume est utilisée, le premier argument donné est l'objet thread:
coroutine.resume(thread1)
-->> honk
Maintenant, la coroutine exécute le code et se termine, changeant son état en mort , ce qui ne peut pas être repris.
print(coroutine.status(thread1))
-->> dead
Coroutines peut suspendre son exécution et la reprendre plus tard grâce à la fonction coroutine.yield :
thread2 = coroutine.create(function()
for n = 1, 5 do
print("honk "..n)
coroutine.yield()
end
end)
Comme vous pouvez le voir, coroutine.yield () est présent dans la boucle for, maintenant, lorsque nous reprendrons la coroutine, il exécutera le code jusqu'à ce qu'il atteigne un coroutine.yield:
coroutine.resume(thread2)
-->> honk 1
coroutine.resume(thread2)
-->> honk 2
Une fois la boucle terminée, l'état du fil devient mort et ne peut plus être repris. Coroutines permet également l'échange entre les données:
thread3 = coroutine.create(function(complement)
print("honk "..complement)
coroutine.yield()
print("honk again "..complement)
end)
coroutine.resume(thread3, "stackoverflow")
-->> honk stackoverflow
Si la coroutine est à nouveau exécutée sans arguments supplémentaires, le complément sera toujours l'argument du premier résumé, dans ce cas "stackoverflow":
coroutine.resume(thread3)
-->> honk again stackoverflow
Enfin, quand une coroutine se termine, toutes les valeurs renvoyées par sa fonction vont au résumé correspondant:
thread4 = coroutine.create(function(a, b)
local c = a+b
coroutine.yield()
return c
end)
coroutine.resume(thread4, 1, 2)
print(coroutine.resume(thread4))
-->> true, 3
Les coroutines sont utilisées dans cette fonction pour renvoyer des valeurs à un thread appelant depuis un appel récursif.
local function Combinations(l, r)
local ll = #l
r = r or ll
local sel = {}
local function rhelper(depth, last)
depth = depth or 1
last = last or 1
if depth > r then
coroutine.yield(sel)
else
for i = last, ll - (r - depth) do
sel[depth] = l[i]
rhelper(depth+1, i+1)
end
end
end
return coroutine.wrap(rhelper)
end
for v in Combinations({1, 2, 3}, 2) do
print("{"..table.concat(v, ", ").."}")
end
--> {1, 2}
--> {1, 3}
--> {2, 3}
Les coroutines peuvent également être utilisées pour une évaluation paresseuse.
-- slices a generator 'c' taking every 'step'th output from the generator
-- starting at the 'start'th output to the 'stop'th output
function slice(c, start, step, stop)
local _
return coroutine.wrap(function()
for i = 1, start-1 do
_ = c()
end
for i = start, stop do
if (i - start) % step == 0 then
coroutine.yield(c())
else
_ = c()
end
end
end)
end
local alphabet = {}
for c = string.byte('a'), string.byte('z') do
alphabet[#alphabet+1] = string.char(c)
end
-- only yields combinations 100 through 102
-- requires evaluating the first 100 combinations, but not the next 5311633
local s = slice(Combinations(alphabet, 10), 100, 1, 102)
for i in s do
print(table.concat(i))
end
--> abcdefghpr
--> abcdefghps
--> abcdefghpt
Les coroutines peuvent être utilisées pour les constructions de tuyauterie comme décrit dans Programmation en Lua . L'auteur de PiL, Roberto Ierusalimschy, a également publié un article sur l'utilisation de coroutines pour mettre en œuvre des mécanismes de contrôle de flux plus avancés et généraux, comme les continuations.