Lua 协同程序(coroutine)
创始人
2024-05-20 00:43:41
0

Lua 协同程序(coroutine)

参考文章:

菜鸟教程

https://zhuanlan.zhihu.com/p/480357405

https://zhuanlan.zhihu.com/p/76249973

  Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

  协同有非常强大的功能,但是用起来也很复杂。

线程和协同程序的区别

  线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起(yield)的时候才会被挂起。

  协同程序有点类似于同步的多线程,在等待一个线程锁的几个线程有点类似协同。

基础语法

方法描述
coroutine.create()创建coroutine,返回coroutine对象,参数为一个匿名函数或函数,当和 resume 配合使用的时候就唤醒函数调用。
coroutine.resume()重启 coroutine,和 create 配合使用。
coroutine.yield()挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果。
coroutine.status()查看 coroutine 的状态。
注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序。
coroutine.wrap()创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复。
coroutine.running()返回正在运行的 coroutine,一个 coroutine 就类似于一个线程,当使用running的时候,就是返回一个 corouting 的线程号。
如果是主协程,返回的结果后面会跟一个true,表示当前的调用coroutine.running()的区域位于主协程中,否则则为false。

TIPS:

上述提到的coroutine不仅仅指create出来的coroutine,同时包括默认的主coroutine。

主协程是指Lua的默认执行线,用coroutine.create创建的协程不是主协程。

协程的基础操作

co = coroutine.create(function()print("123")end
)coroutine.resume(co)   --运行协程,打印123

  新创建的协程并不会运行,而是处于挂起状态,可以通过coroutine.resume让其运行。这是因为Lua提供的是非对称协程asymmertric coroutine),也就是说需要两个函数来控制协程的运行,一个用于挂起协程的执行,另一个用于恢复它的执行。

以下实例演示以上各个方法的用法:

co = coroutine.create(function(i)print(i);end
)coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- deadprint("----------")co = coroutine.wrap(function(i)print(i);end
)co(1)print("----------")co2 = coroutine.create(function()for i=1,10 doprint(i)if i == 3 or i == 2 thenprint(coroutine.status(co2))  --runningprint(coroutine.running()) --thread:XXXXXX false表示在协程中,true表示在主协程中endcoroutine.yield()endend
)coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3print(coroutine.status(co2))   -- suspended
print(coroutine.running())

输出结果:

效果图

  coroutine.running就可以看出来,coroutine在底层实现就是一个线程。当create一个coroutine的时候就在一个新线程中注册一个事件。当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等待再次resume触发事件。

接下来分析一个更详细的实例:

function foo (a)print("foo 函数输出", a)return coroutine.yield(2 * a) -- 返回  2*a 的值
endco = coroutine.create(function (a , b)print("第一次协同程序执行输出", a, b) -- co-body 1 10local 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("---分割线---")

输出结果:

效果图

  以上实例介绍如下:

  • 一开始调用resume,将协同程序唤醒,resume操作成功返回true,否则返回false;
  • 子协程第一次启动,输出结果;
  • 运行到yield语句;
  • yield首先挂起子协程,并将2*a返回给主协程resume(即第17行运行成功打印true的同时,接收到2*a也进行print输出);
  • 第二次resume,再次唤醒协同程序(此时因为挂起时已经位于第8行的位置,那么重新启动也从该行启动,此时resume的参数中,除了第一个参数,其它参数都会传递给yield,即作为yield的返回值,那么第8行local r = “r”);
  • yield返回;
  • 协同程序继续运行(同理);
  • 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine。

  从上述我们可以得出结论:resume和yield的配合强大之处在于resume处于主协程中,它将外部状态(数据)传入到子协程中的;而yield则将内部的状态(数据)返回到主协程中。

  我们发现resume被调用后,有两种传递方式,一种是匿名函数的参数进行传递,一种是通过yield的返回值进行传递。

通过上述介绍,我们可以得出以下代码的输出结果的合理性:

co = coroutine.create(function(x)print(coroutine.yield())
end
)coroutine.resume(co,1)      --不打印任何值
coroutine.resume(co,1,2)    --打印 1 2

效果图

生产 - 消费者问题

  消费者不需要,则生产者就不生产。消费者一有消费需求,生产者立即生产。

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

输出结果:

效果图

  输出结果由于消费者一直有需求,生产者是一直在生产的。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...