Lua 中的协程分享

2015年03月24日 14:04 0 点赞 0 评论 更新于 2025-11-21 18:25

引言

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 中的协程可以通过 resumeyield 进行数据交换,具体方式如下:

通过 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 协程为开发者提供了一种灵活的方式来处理并发和异步任务,尤其在协作式多任务场景下具有独特的优势。

作者信息

menghao

menghao

共发布了 3994 篇文章