Lua 中的协程分享
引言
Lua 里的协程是一种独特的编程概念,在其他编程语言中也有类似的实现,例如 Python 的 Gevent 就是一个基于协程(coroutine)的网络开发框架,据说其性能表现相当不错。
协程与多线程的区别
调度机制差异
一般多线程的调度由操作系统决定,具有抢占式的特点,即系统会根据自身的调度算法决定哪个线程获得执行权。而协程则不同,它是用户空间线程,操作系统对其存在一无所知,协程的调度需要用户自己来完成。协程中的每个执行单元会主动决定自己何时暂停执行,并将执行权交给下一个协程,这种方式非常适合用于实现协作式多任务。
多核利用情况
在多处理器环境下,多线程程序可以同时运行多个线程,充分利用多核处理器的并行计算能力。而协同程序是通过协作来完成任务的,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在必要时才会被挂起。因此,Lua 的协程在多核场景下无法像多线程那样直接利用多核技术。
Lua 协程的状态与创建
协程的状态
Lua 协程有三种状态:挂起态(suspended)、运行态(running)、停止态(dead)。我们可以使用 coroutine.status 函数来查看协程当前所处的状态。
协程的创建
创建一个协程需要调用 coroutine.create 函数,该函数只接收单个参数,这个参数是协程的主函数。coroutine.create 函数仅仅是创建一个新的协程,并返回一个类型为 thread 的对象,它并不会启动协程的运行。以下是一个简单的示例:
hxc = coroutine.create(function ()
print("hi coroutine")
end)
print(type(hxc)) -- 输出: thread
print(coroutine.status(hxc)) -- 输出: suspended
启动协程
要启动一个处于挂起状态的协程,需要使用 coroutine.resume 函数。该函数会使协程从挂起状态变为运行态,当协程执行完毕后,会进入停止态。调用 coroutine.resume 时,传入的第一个参数就是 coroutine.create 的返回值,后续传入的参数将被传递给协程的主函数。示例如下:
coroutine.resume(hxc) -- 输出: hi coroutine
print(coroutine.status(hxc)) -- 输出: dead
协程的挂起与继续执行
挂起协程
yield 函数是协程的一个特别之处,它可以将正在运行的协程代码挂起。当协程开始运行后,会一直执行,直到遇到 yield 调用或者协程自身终止。以下是一个包含 yield 的示例:
hxc = coroutine.create(function ()
for i = 1, 10 do
print("iter", i)
coroutine.yield()
end
end)
继续执行协程
执行上述协程时,程序将在第一个 yield 处被挂起:
coroutine.resume(hxc) -- 输出: iter 1
print(coroutine.status(hxc)) -- 输出: suspended
再次调用 coroutine.resume 可以激活被挂起的协程,协程将从 yield 的位置继续执行,直到再次遇到 yield 或者程序结束:
coroutine.resume(hxc) -- 输出: iter 2
协程的数据交换
Lua 中的协程可以通过 resume 和 yield 进行数据交换,具体方式如下:
通过 resume 传递参数给协程主程序
hxc = coroutine.create(function (a, b)
print("hxc", a, b)
end)
coroutine.resume(hxc, 1, 2) -- 输出: hxc 1 2
通过 yield 传递数据给 resume
coroutine.resume 函数的返回值中,第一个值为 true 表示调用成功,true 之后的部分即是 yield 的参数。示例如下:
hxc = coroutine.create(function (a, b)
coroutine.yield(a + b, a - b)
end)
print(coroutine.resume(hxc, 20, 10)) -- 输出: true 30 10
将 resume 的参数传递给 yield
hxc = coroutine.create (function ()
print("hxc", coroutine.yield())
end)
coroutine.resume(hxc)
coroutine.resume(hxc, 4, 5) -- 输出: hxc 4 5
协程的用途
协程的一个明显用途是在需要访问某个异步功能时。在 C 语言中,通常采用回调的方法来处理异步操作,即当异步操作完成时,调用脚本中的一个已知函数。而协程可以将异步过程当作同步处理,当程序执行到异步点时,协程会挂起,当异步操作完成后,协程会从挂起的位置继续执行。
综上所述,Lua 协程为开发者提供了一种灵活的方式来处理并发和异步任务,尤其在协作式多任务场景下具有独特的优势。