Lua : Statégie de Coroutine

Par lcirpaci, 6 février, 2025
Lua logo
Stratégie de Coroutines en Lua

Pourquoi parler de stratégie de coroutine et non de coroutine en Lua ?

La raison principale est que le concept réel de coroutine n’existe pas tout à fait en Lua. En effet, les coroutines en Lua sont monothread, autrement dit asymétriques.

Contrairement aux threads traditionnels, qui permettent une exécution parallèle, les coroutines en Lua fonctionnent dans un seul thread et exigent une gestion explicite du passage de contrôle. Cela signifie qu'une coroutine ne reprend son exécution que lorsqu'elle est explicitement réactivée par l'appelant via coroutine.resume(). Inversement, une coroutine suspend son exécution volontairement en appelant coroutine.yield().

Une gestion explicite du passage de contrôle

L'utilisation des coroutines repose donc sur une stratégie bien définie pour organiser la coopération entre différentes parties du programme. Par exemple, elles sont souvent employées pour :

  • Implémenter des itérateurs
  • Mettre en place des systèmes de gestion d'événements
  • Créer des mécanismes de planification de tâches non bloquantes style une requête web asynchrone

Stratégie d'utilisation des coroutines

Une bonne stratégie d'utilisation des coroutines en Lua implique de structurer son code de manière à minimiser les points de blocage et à garantir une reprise fluide de l'exécution. Par exemple, dans un jeu vidéo, les coroutines peuvent être utilisées pour gérer :

  • Des animations
  • Des comportements d'intelligence artificielle

Cela permet d'éviter de bloquer le fil principal d'exécution.

Simuler de la concurrence

Bien que Lua n’offre pas de multitraitement natif, il est possible de combiner les coroutines avec des bibliothèques externes ou des threads en C pour simuler une exécution concurrente. Cependant, cela demande une gestion soigneuse des ressources partagées et une coordination efficace entre les différents processus en cours d'exécution.

La bibliothèque coroutine permet de créer, démarrer et suivre des routines dans le cadre de la gestion de l'exécution concurrente en Lua.

1. coroutine.create(function : Function)

Description

Cette fonction permet de créer une routine à partir d'une fonction.
Une routine représente une séquence d'exécution qui peut être mise en pause et reprise.

Paramètres

  • function : Une fonction Lua qui contient le code à exécuter dans la routine.

Retour

  • Retourne un objet Routine qui représente la routine créée.

Example

local routine = coroutine.create(function()
    print("Cette routine est exécutée !")
end)

2. coroutine.resume(routine:Routine,args:...?)

Description

Cette fonction permet d'executé une routine

Paramètres

  • routine : la routine à exécuter
  • args : un ensemble d'argument associer à la fonction dans la routine ou des données injecter dans la pause précédent

Retours

  • Succès d'éxécution de la routine
  • Message d'erreur de la routine (falcultatif)
  • args un ensemble d'argument associer aux des données injecter dans la pause précédent (falcultatif)

Example simple

local routine = coroutine.create(function(qui,message)
    print(qui,message)
end)
Coroutine.resume(routine,"Yoda","Bonjour  maître Yoda")
Yoda    Bonjour  maître Yoda

Example avec une erreur

local routine = coroutine.create(function(qui,message)
    print(qui,message)
end)
coroutine.resume(routine,"Yoda","Bonjour maître Yoda")
local estBienExecuter, messageErreur = coroutine.resume(routine)
print(estBienExecuter, messageErreur)
Yoda    Bonjour maître Yoda
false   cannot resume dead coroutine

3.coroutine.status(routine:Routine)

Description

Permet d'obtenir le status de la Routine

  • suspended : la routine est disponible, mais non utilisé
  • running : la routine est en cours d'exécution
  • normal : lorsque la routine est lancer par une autre
  • dead : la routine est morte

Paramètres

  • Routine : la routine à exécuter

Retour

  • Status : suspended, running ou dead

Examples de cycle de vie d'une routine

local status = {
    avant = nil,
    pendant = nil,
    apres = nil
}
local routine, bienvenue
bienvenue = function(nom)
    print("Bonjour",nom)
    status.pendant = coroutine.status(routine)
end
routine = coroutine.create(bienvenue)
status.avant = coroutine.status(routine)
coroutine.resume(routine,"Léo")
status.apres = coroutine.status(routine)
for etape,etat in pairs(status) do print(etape,etat) end
Bonjour Léo
avant   suspended
pendant running
apres   dead

4. coroutine.yield(args:...?)

Description

Permet de mettre en pause une routine pour son prochain apelle.
Permet l'échapement et l'injection de données vers une Routine

Paramètres

args : un ensemble d'argument associer à la fonction dans les pareméetre de yield pour l'éhcapement et comme assignation pour l'injection(falcultatif)w

Retour

args : un ensemble d'argument associer à la fonction dans les pareméetre de yield pour l'éhcapement (falcultatif)
pause de la routine

Conseil

pour savoir le nombre de coroutine.resume à faire. compter le nombre de coroutine.yield + 1 dans votre fonction.

Example échapement

local routine = coroutine.create(function()
    coroutine.yield("données échaper")
end)
local estExecuter, donneExtrapoler = coroutine.resume(routine)
print(donneExtrapoler)
données échaper

Example injection

local routine = coroutine.create(function()
    local x = coroutine.yield()
    print(x)
end)
coroutine.resume(routine)
coroutine.resume(routine,"Données injecter")
Données injecter

5. coroutine.wrap(function:Function)

Description

Permet de créer une fonction avec un comportement de routine sans être une routine

Paramètres

  • Fonction : la fonction avec les pause

Retour

  • args un ensemble d'argument associer aux des données injecter dans la pause précédent (falcultatif)

Example

routine = coroutine.wrap(function()
    print("Étape 1")
    local reponse = coroutine.yield("Bonjour maître Yoda")
    print("Étape 2 - "..reponse)
    coroutine.yield()
    print("Étape 3")
end)
local data = routine()
print(data)
routine("Bonjour Luc")
routine()
Étape 1
Bonjour maître Yoda
Étape 2 - Bonjour Luc
Étape 3

6. coroutine.close(routine:Routine)

Description

permet de tuer votre routine

Paramètres

  • Routine : la routine à tuer

Retour

  • boolean : true si la routine existe

Example

local routine = coroutine.create(function()
    print("Dans la coroutine, c'est :", 
        coroutine.running()-- Affiche l'ID de la coroutine
    )
end)
coroutine.close(routine)
print(coroutine.resume(routine))
false   cannot resume dead coroutine

Références

Étiquettes

Commentaires1

hpierre

il y a 2 mois 1 semaine

Salut Léo, ton article sur les coroutines en Lua est vraiment bien ficelé ! J'apprécie particulièrement la façon dont tu as structuré l'information pour la rendre accessible, même pour quelqu'un qui débute avec ce concept. Ta présentation des coroutines comme étant monothread et asymétriques est un point fort. Ça aide vraiment à comprendre pourquoi on parle de "stratégie de coroutine" plutôt que de multitâche concurrent. Bien joué ! J'ai trouvé très utile ton explication détaillée des fonctions de la bibliothèque coroutine, avec des exemples concrets à l'appui. Ça rend le tout beaucoup plus tangible. Ce que j'ai particulièrement apprécié : Ta distinction claire entre coroutines et threads classiques. Ça évite pas mal de confusions ! Tes exemples de code sont limpides et illustrent parfaitement le fonctionnement de chaque fonction. J'ai adoré la partie sur les cas d'utilisation concrets (animations, IA, requêtes asynchrones). Ça donne vraiment envie d'essayer ! Bref, c'est un super article qui m'a permis de mieux saisir l'intérêt et le fonctionnement des coroutines en Lua. Merci pour ce travail de qualité, Léo !