Coroutine Lua



Qu'est-ce que la coroutine ?

La coroutine Lua est similaire à un thread : elle possède une pile indépendante, des variables locales indépendantes et un pointeur d'instruction indépendant, tout en partageant des variables globales et la plupart des autres choses avec d'autres coroutines.

La collaboration est une fonction très puissante, mais elle est aussi très compliquée à utiliser.

La différence entre les threads et les coroutines

La principale différence entre les threads et les coroutines est qu'un programme avec plusieurs threads peut exécuter plusieurs threads en même temps, tandis que les coroutines doivent s'exécuter en coopération avec chacun autre.

Une seule coroutine est en cours d'exécution à un moment donné, et la coroutine en cours d'exécution ne sera suspendue que lorsqu'on lui demandera explicitement de la suspendre.

Les coroutines sont quelque peu similaires aux multi-threads synchronisés, et plusieurs threads attendant le même thread lock sont quelque peu similaires à la collaboration.

Grammaire de base

方法描述
coroutine.create()创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
coroutine.resume()重启coroutine,和create配合使用
coroutine.yield()挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
coroutine.status()查看coroutine的状态
注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序
coroutine.wrap()创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
coroutine.running()返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

Les exemples suivants démontrent l'utilisation de chacune des méthodes ci-dessus :

-- coroutine_test.lua 文件
co = coroutine.create(
    function(i)
        print(i);
    end
)
 
coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- dead
 
print("----------")
 
co = coroutine.wrap(
    function(i)
        print(i);
    end
)
 
co(1)
 
print("----------")
 
co2 = coroutine.create(
    function()
        for i=1,10 do
            print(i)
            if i == 3 then
                print(coroutine.status(co2))  --running
                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))   -- suspended
print(coroutine.running())
 
print("----------")

Le résultat de l'exécution des exemples ci-dessus est :

1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868	false
suspended
thread: 0x7fb801c04c88	true
----------

On peut voir à partir de coroutine.running que le sous-jacent l'implémentation de la coroutine est un fil.

Lors de la création d'une coroutine, un événement est enregistré dans un nouveau fil de discussion.

Lorsque la reprise est utilisée pour déclencher un événement, la fonction coroutine de create est exécutée. Lorsque le rendement est rencontré, cela signifie suspendre le thread en cours et attendre que la reprise déclenche à nouveau l'événement.

Ensuite, nous analysons un exemple plus détaillé :

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
 
co = coroutine.create(function (a , b)
    print("第一次协同程序执行输出", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("第二次协同程序执行输出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
     
    print("第三次协同程序执行输出", r, s)
    return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

Le résultat de l'exécution de l'exemple ci-dessus est :

第一次协同程序执行输出	1	10
foo 函数输出	2
main	true	4
--分割线----
第二次协同程序执行输出	r
main	true	11	-9
---分割线---
第三次协同程序执行输出	x	y
main	true	10	结束协同程序
---分割线---
main	false	cannot resume dead coroutine
---分割线---

L'exemple ci-dessus continue comme suit :

  • Appelez CV pour réveiller la coroutine. L'opération de reprise renvoie vrai en cas de succès, sinon elle renvoie faux

  • La coroutine s'exécute

  • < ; 🎜>
  • Exécutez l'instruction Yield ;

  • Yield raccroche la coroutine et le premier CV revient (Remarque : le rendement revient ici, et les paramètres sont les paramètres du CV ; )

  • Reprendre une deuxième fois et réveiller à nouveau la coroutine (Remarque : parmi les paramètres de reprise ici, à l'exception du premier paramètre, les paramètres restants seront utilisés comme paramètres de rendement)

  • le rendement revient ;

  • La coroutine continue de fonctionner

  • Si la coroutine utilisée continue ; pour s'exécuter et continue d'appeler la méthode de reprise une fois terminé, puis Sortie : impossible de reprendre la coroutine morte

La puissante combinaison de reprise et de rendement est que la reprise est dans le processus principal et qu'elle passe statut externe (données) dans la coroutine ; et rendement Le statut interne (données) est renvoyé au processus principal.


Problème producteur-consommateur

Maintenant, j'utilise la coroutine de Lua pour résoudre le problème classique producteur-consommateur.

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()

Le résultat de sortie de l'exemple ci-dessus est :

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