Lua 协程

一、协程

Lua 中使用半协程的方式进行组织代码。

和线程的最大区别在于,一个多线程程序可以并行运行多个线程,而协程却需要彼此协作运行,即任意指定时刻只能一个协程运行,且只有当正在运行的协程显式地要求被挂起时,该协程才会被暂停。

二、协程的创建和使用

Lua 的协程创建非常简单,只需要通过 coroutine.create(f) 函数即可创建,但是创建完的协程并不会自动运行,还需要使用 coroutine.resume(co, val1, ...) 函数进行运行。

1、coroutine.create(f)

创建一个新的协程,主体为 f 函数。

参数:

  • f:是一个 Lua 函数,即协程运行的代码。

返回值:

返回新创建的协程,是一个类型为 thread 的对象

举个例子

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
print("type(coroutine)", type(co))      --> type(coroutine)	thread

2、coroutine.resume(co, val1, …)

开始或继续协程 co 的执行。

当对协程 co 第一次使用 resume 启动协程时,会开始运行它的主体,会将 (val1, …) 作为参数传递给主体函数。

如果协程 co 已经挂起(通过 yield 进行挂起,下面会分享),则 resume 会重新启动它(从挂起点开始运行),并将 (val1, …) 作为 yield 的参数传递。

参数:

  • co:要被启动或重新启动的协程
  • val1,…:“作为启动的参数传递给协程” 或 “作为 yield 的接受参数”。

返回值:

  • 如果协程运行没有任何错误,则会返回 true 加上 “yield 函数抛出的参数值” 或是 “当协程终止时协程主体函数返回的值”。
  • 如果有任何错误,则返回 false 加上错误信息。

举个例子

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
coroutine.resume(co)
--> Hello, jiang pengyong.

三、协程的状态

协程总共有四种状态:挂起(suspended)、运行(running)、正常(normal)、死亡(dead)。

可以通过 coroutine.status(co) 进行查看协程的状态。

1、coroutine.status(co)

获取协程 co 的状态,返回值以字符串表示:

  • running:协程正在运行
  • suspended:协程在调用 yield 时被挂起,或者还没有开始运行
  • normal:协程处于活动状态但未运行,即它已启动另一个协程(在该协程内调用了 resume 启动另一个协程)
  • dead:协程已完成其主体功能,或者因错误停止

下面一一阐述各种状态:

2、挂起(suspended)

处于这一状态的两种情况:

  • 第一种情况:协程刚被创建时(协程不会自动运行),该协程就处于这个状态。
  • 第二种情况:协程内部调用了 yield 方法,将自身挂起,此时也处于这个状态。

刚创建的协程

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
print("status(coroutine)", coroutine.status(co))    --> status(coroutine)	suspended

使用 yield 进行挂起

local co = coroutine.create(function()
    coroutine.yield()
end)
coroutine.resume(co)
print(coroutine.status(co))     --> suspended

3、运行(running)

当一个协程被运行起来后,他的状态就为运行状态了

co = coroutine.create(function()
    print(coroutine.status(co))         --> running
    print("Hello, jiang pengyong.")     --> Hello, jiang pengyong.
end)
coroutine.resume(co)

4、正常(normal)

当一个协程 A 内部启动了另一个协程 B ,此时 A 处于正常状态, B 处于运行状态

local B
local A = coroutine.create(function()
    print("run A")              --> run A
    coroutine.resume(B)
    print("end A")              --> end A
end)
B = coroutine.create(function()
    print("start B")            --> start B
    print("A status", coroutine.status(A))      --> A status	normal
    print("B status", coroutine.status(B))      --> B status	running
    print("end B")              --> end B
end)
coroutine.resume(A)

5、死亡(dead)

当协程执行完了函数,就是这状态

co = coroutine.create(function()
    print(coroutine.status(co))                 --> running
    print("Hello, jiang pengyong.")             --> Hello, jiang pengyong.
end)
coroutine.resume(co)                            
print("status(coroutine)", coroutine.status(co))    --> status(coroutine)	dead

四、coroutine.running()

可以通过 coroutine.running() 进行获取当前的协程

返回值:

有返回两个值:

  • 第一个是正在运行的协程。
  • 第二个是一个布尔值,表示正在运行的协程是否为主协程,为主协程时为真。
print("coroutine.running() 主协程", coroutine.running())        --> coroutine.running() 主协程	thread: 0x7f8f1d808e08	true
local co = coroutine.create(function()
    print("coroutine.running()", coroutine.running())           --> coroutine.running()	thread: 0x6000020e4278	false
end)
print(coroutine.resume(co))

五、协程的传值

协程的传值由两个函数进行 resumeyieldresume 已经在前面介绍了,这里介绍下 yield

1、coroutine.yield(…)

挂起当前协程,yield 会将参数(…)都作为结果传递给 resume

2、使用

local co = coroutine.create(function(x)
    print("接收第一次参数:", x)
    a, b, c, d, e = coroutine.yield("第一次返回值")
    print("接收第二次参数:", a, b, c, d, e)
    return "第二次返回值"
end)
print(coroutine.resume(co, "hi"))
print(coroutine.resume(co, 4, 5, "江澎涌"))

--> 接收第一次参数:	hi
--> true	第一次返回值
--> 接收第二次参数:	4	5	江澎涌	nil	nil
--> true	第二次返回值

当协程还没有启动的时候,则会将 resume 第二个开始携带的参数当作协程的方法参数。

当协程内部调用 yield 时,协程会挂起。在挂起协程的同时,将 yield 的参数作为返回值给到外部启动该协程的 resume

当外部再次启动协程时,同样会将 resume 第二个开始携带的参数当作入参给到 yield 函数,可以理解为 yield 的返回值。

最后当协程方法运行完后,如果有 return 将值返回,则会被用作 resume 的返回值。

3、一图胜千言

可以通过下图理解 createresumeyield 即协程返回值间的关系

Lua 协程_第1张图片

六、对协程异常的捕获

resume 方法是运行在保护模式中的,所以一旦协程内部发生错误,则会抛给 resume 方法,resume 方法会得到两个返回值,一个是 false,一个是错误原因。

local co = coroutine.create(function()
    error("协程内部错误")
    coroutine.yield()
end)
print(coroutine.resume(co))     --> false	...ong/Desktop/study/lua_study_2022/17 协程/coroutine.lua:50: 协程内部错误
print(coroutine.status(co))     --> dead

七、coroutine.wrap(f)

wrap 和 create 有些类似,只是 wrap 返回的不是协程,而是一个函数,每次调用这个函数,则会相当于这个协程被 resume 了一次。还有当协程方法内部发生异常,则会导致抛出异常,不会被保护模式所获取。

对于传值方面,和 yield-resume 是一样的。

local co = coroutine.wrap(function(name)
    print(string.format("Hello, %s.", name))    --> Hello, jiang pengyong.
    local age = coroutine.yield("Hi.")          --> Hi.
    print(age)                                  --> 29
    return "Bye."
end)
print(co("jiang pengyong"))     --> Hi.
print(co("29"))                 --> Bye.

1、coroutine.wrap(f) 和 coroutine.create(f)

coroutine.wrap(f) 使用简单,只是一个唤醒函数。

coroutine.create(f) 可以更加灵活的控制,例如可以获取协程的状态,捕获异常。

八、coroutine.isyieldable()

检测正在运行的协程是否可以挂起,如果可以挂起则为 true。

如果运行中的协程不是主线程并且不在不能挂起的 C 函数中,则他是可挂起的。

local co = coroutine.create(function()
    print(coroutine.isyieldable())      --> true
    print("Hello, jiang pengyong.")     --> Hello, jiang pengyong.
end)
coroutine.resume(co)                    
print(coroutine.isyieldable())          --> false

九、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。

你可能感兴趣的:(Lua,lua,android,开发语言,c++,c语言)