English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua 协同程序(coroutine)

What is a coroutine?

Lua coroutines (coroutines) are similar to threads: they have their own stack, their own local variables, their own instruction pointer, and at the same time, they share global variables and most other things with other coroutines.

Coroutines are a very powerful feature, but they are also very complex to use.

Thread and coroutine differences

La principale différence entre un thread et une coroutine réside en ce que, un programme avec plusieurs threads peut exécuter plusieurs threads en même temps, tandis que les coroutines doivent fonctionner en collaboration les unes avec les autres.

À tout moment donné, une seule coroutine est en cours d'exécution, et la coroutine en cours d'exécution ne peut être suspendue que lorsque explicitement demandée

Les coroutines sont un peu similaires aux threads synchronisés, plusieurs threads en attente du même verrou de thread sont un peu similaires aux coroutines.

Syntaxe de base

MéthodeDescription
coroutine.create()Crée une coroutine, retourne une coroutine, le paramètre est une fonction, lorsque utilisé avec resume, il réveille l'appel de la fonction
coroutine.resume()Redémarrez la coroutine, utilisé avec create
coroutine.yield()Suspendez la coroutine, mettez la coroutine en état suspendu, cela peut être très utile lorsqu'il est utilisé avec resume
coroutine.status()Vérifiez l'état de la coroutine
Note : les états de coroutine sont trois : dead, suspended, running, quand ils prennent ces états spécifiques, veuillez consulter le programme suivant
coroutine.wrap()Crée une coroutine, retourne une fonction, une fois que vous appelez cette fonction, vous entrez dans la coroutine, fonctionnalité redondante avec create
coroutine.running()Retourne la coroutine en cours d'exécution, une coroutine est une thread, lorsque l'on utilise running, on retourne le numéro de thread de la coroutine

Le présent exemple montre l'utilisation de ces méthodes :

-- fichier coroutine_test.lua
co = coroutine.create(
    function(i)
        print(i);
    end
)
 
coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- mort
 
print("----------")
 
co = coroutine.wrap(
    function(i)
        print(i);
    end
)
 
co(1)
 
print("----------")
 
co2 = coroutine.create(
    function()
        pour i=1,10 faire
            print(i)
            si i == 3 ensuite
                print(coroutine.status(co2))  --en cours d'exécution
                print(coroutine.running()) --thread:XXXXXX
            end
            coroutine.yield()
        end
    end
)
 
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
 
print(coroutine.status(co2))   -- suspendu
print(coroutine.running())
 
print("----------")

以上示例执行输出结果为:

1
mort
----------
1
----------
1
2
3
en cours d'exécution
thread: 0x7fb801c05868    faux
suspendu
thread: 0x7fb801c04c88    true
----------

coroutine.running montre que, dans la couche inférieure, coroutine est implémenté comme un thread.

Lorsque l'on crée une coroutine, c'est comme si on enregistrait un événement dans un nouveau thread.

Lorsque resume est déclenché par l'événement, la fonction coroutine create est exécutée, et lorsqu'elle rencontre yield, cela signifie que le thread courant est suspendu en attendant que resume déclenche à nouveau l'événement.

Analysons maintenant un exemple plus détaillé :

function foo (a)
    print("Sortie de la fonction foo", a)
    return coroutine.yield(2 * a) -- Retour  2*La valeur de a
end
 
co = coroutine.create(function (a , b)
    print("Sortie de l'exécution de la première coroutine", a, b) -- co-corps 1 10
    local r = foo(a + 1)
     
    print("Sortie de l'exécution de la deuxième coroutine", r)
    local r, s = coroutine.yield(a + b, a - b)  -- Les valeurs de a, b sont les paramètres passés lors du premier appel de la coroutine
     
    print("Sortie de l'exécution de la troisième coroutine", r, s)
    return b, "Terminer la coroutine"                   -- La valeur de b est le paramètre passé lors de la deuxième appel de la coroutine
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--Ligne de séparation----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---Ligne de séparation---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---Ligne de séparation---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---Ligne de séparation---")

以上示例执行输出结果为:

Sortie de l'exécution de la première coroutine    1    10
Sortie de la fonction foo    2
main    true    4
--Ligne de séparation----
Sortie de l'exécution de la deuxième coroutine    r
main    true    11    -9
---Ligne de séparation---
Sortie de l'exécution de la troisième coroutine    x    y
main    true    10    Terminer la coroutine
---Ligne de séparation---
main    false    cannot resume dead coroutine
---Ligne de séparation---

L'exemple suivant continue ainsi :

  • Appeler resume pour réveiller la coroutine, si l'opération resume réussit, retourne true, sinon retourne false;

  • La coroutine s'exécute;

  • Exécutez jusqu'à la phrase yield;

  • Le verbe yield suspend la coroutine, la première fois que resume est appelée, il retourne;(Remarque : ici, yield retourne, le paramètre est celui de resume)

  • 第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)

  • yield返回;

  • 协同程序继续运行;

  • 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine

resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。

生产者-消费者问题

现在我就使用Lua的协同程序来完成生产者-消费者这一经典问题。

local newProductor
function productor()
     local i = 0
     while true do
          i = i + 1
          send(i)     -- 将生产的物品发送给消费者
     end
end
function consumer()
     while true do
          local i = receive()     -- 从生产者那里得到物品
          print(i)
     end
end
function receive()
     local status, value = coroutine.resume(newProductor)
     return value
end
function send(x)
     coroutine.yield(x)     -- x表示需要发送的值,值返回以后,就挂起该协同程序
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()

以上示例执行输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
……