【Lua学习笔记】Lua进阶——协程

文章目录


协程

协程是一种并发操作,相比于线程,线程在执行时往往是并行的,并且线程在创建销毁执行时极其消耗资源,并且过长的执行时间会造成主进程阻塞。而协程可以以并发时轮值时间片来执行,优点是不会阻塞,消耗资源少,可以手动控制。至于协程和线程的区别,什么是并发并行,请自行查阅或者学习操作系统理论知识。

协程的定义和调度

在Unity中,由于游戏是单线程的(这是为了其他线程不阻塞进程),因此我们常常需要使用协程。在Lua中也支持协程,例如下列代码:

lua 复制代码
Corou1 =coroutine.create(function ()
    print("我是协程1")
end)
print(Corou1)
Corou2 = coroutine.wrap(function ()
    print("我是协程2")
end)
print(Corou2)
coroutine.resume(Corou1) --开始或继续协程
Corou2()   	--运行函数
输出:
thread: 00CAEC00
function: 00CA0578
我是协程1
我是协程2

我们可以用两种不同的方法创建协程,不过返回值一个是协程(虽然thread是线程),一个是函数。所以对这两个协程的运行方式也不同。如何理解这两种方式?我想用C#的例子:

csharp 复制代码
void StartCorou()  --function类型
{
    StartCoroutine(Coroutine());  --thread类型直接执行
}

IEnumerator Coroutine()
{
}

thread类型定义的就是协程本身,因此我们可以用lua提供的协程table中的方法来调用,而function类型更像是定义了一个启动函数,通过调用这个函数来执行。

在Lua中的协程有一个优点:我们可以手动执行

lua 复制代码
Corou1 =coroutine.create(function ()
    local i = 1
    while true do
        print(i)
        i = i + 1
        coroutine.yield(i)  --yield也可以返回
    end
end)

coroutine.resume(Corou1)
a,b = coroutine.resume(Corou1)
print(a,b)
coroutine.resume(Corou1)

输出:
1
2
true	3
3 

通过注释我们可以发现,resume第一个返回固定为成功判断,然后返回yield提供的变长参数
function coroutine.resume(co: thread, val1?: any, ...any)
  -> success: boolean
  2. ...any

可以看到,对于进入了yield的协程,需要我们手动执行resume才能继续执行。而非自动地轮换时间片。这样有利有弊,但我认为在调度上更加灵活了。

使用wrap的话:

lua 复制代码
function f1()
    local i = 1
    while true do
        print(i)
        i = i + 1
        coroutine.yield(i) 
    end
end
b = coroutine.wrap(f1)
print("返回值"..b())
print("返回值"..b())
print("返回值"..b())

输出:
1
返回值2
2
返回值3
3
返回值4

function coroutine.wrap(f: fun(...any):...unknown)
  -> fun(...any):...unknown

使用wrap则我们发现,wrap并不返回成功判断,而是直接返回yield给出的变长参数,这是由于其内定义的函数就只接收变长参数。


Status

Lua中的协程总共有四种状态:

lua 复制代码
function coroutine.status(co: thread)
  -> "dead"|"normal"|"running"|"suspended"
以字符串形式返回协程 co 的状态。

return #1:
    | "running"  正在运行。
    | "suspended"  挂起或是还没有开始运行。
    | "normal"  是活动的,但并不在运行。
    | "dead"  运行完主体函数或因错误停止。

让我们从下面的例子看看协程什么时候会进入这些状态:

lua 复制代码
C2 =  coroutine.create(function ()
    print("协程2执行")
    print("此时协程1:"..coroutine.status(Corou1))
    coroutine.yield()
end)
Corou1 =coroutine.create(function ()
    local i = 1
    while i<3 do
        print(i)
        i = i + 1
        if i==2 then
            coroutine.resume(C2)
        end
        print(coroutine.status(Corou1))
        coroutine.yield() 
    end
end)
print(coroutine.status(Corou1))
print("######")
coroutine.resume(Corou1)
print(coroutine.status(Corou1))
print("######")
coroutine.resume(Corou1)
print(coroutine.status(Corou1))
print("######")
coroutine.resume(Corou1)
print(coroutine.status(Corou1))
print("######")

输出:
suspended
######
1
协程2执行
此时协程1:normal
running
suspended
######
2
running
suspended
######
dead
######

从上述例子中看到,当协程未启动以及暂停中等待下一次resume时,其状态是suspended。而当协程正在执行时状态是running,当协程1执行切换到协程2时,协程1的状态是normal。最后当协程1退出时其状态是dead。


Running

running函数可以得到当前正在运行的协程(的地址)。

lua 复制代码
function coroutine.running()
  -> running: thread
  2. ismain: boolean
返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
(我试了一下没接收到这个bool,不知道什么情况)

用例:

lua 复制代码
Corou1 =coroutine.create(function ()
    local i = 1
    while i<3 do
        print(i)
        i = i + 1
        print("协程1状态:"..coroutine.status(Corou1))
        print(coroutine.running())
        coroutine.yield() 
    end
end)
coroutine.resume(Corou1)

输出:
1
协程1状态:running
thread: 00A7E1F8  <-- 协程1的地址

先抛出一个问题:如果在上述的同样代码中,执行协程2时print这个running函数,请问可以得到什么?考虑到协程1在协程2运行时是normal状态,当前运行(running)的协程应当是协程2

lua 复制代码
C2 =  coroutine.create(function ()
    print("协程2执行")
    print(coroutine.running())
    print("此时协程1:" .. coroutine.status(Corou1))
    print("协程2执行结束")
    coroutine.yield()
end)
Corou1 =coroutine.create(function ()
    local i = 1
    while i<3 do
        print(i)
        i = i + 1
        if i==2 then
            coroutine.resume(C2)
        end
        print("协程1状态:"..coroutine.status(Corou1))
        print(coroutine.running())
        coroutine.yield() 
    end
end)
coroutine.resume(Corou1)
coroutine.resume(Corou1)

输出:
1
协程2执行
thread: 00C4CFE8
此时协程1:normal
协程2执行结束
协程1状态:running
thread: 00C4D408
2
协程1状态:running
thread: 00C4D408
相关推荐
饭碗、碗碗香1 小时前
【Dify学习笔记】:Dify搭建表单信息提交系统
人工智能·笔记·学习·ai
哈基米喜欢哈哈哈1 小时前
Uber的MySQL实践(一)——学习笔记
数据库·笔记·后端·mysql
jz_ddk2 小时前
[科普] AI加速器架构全景图:从GPU到光计算的算力革命
人工智能·学习·算法·架构
峥嵘life3 小时前
Android初学者系统开发学习路线参考
android·学习
-风中叮铃-4 小时前
【MongoDB学习笔记1】MongoDB的常用命令介绍-数据库操作、集合操作、文档操作、文档分页查询、高级查询
数据库·学习·mongodb
生医转码,四海为家4 小时前
零基础-动手学深度学习-9.7. 序列到序列学习(seq2seq)
学习
野蛮人6号4 小时前
MySQL笔记
数据库·笔记·mysql
snowfoootball4 小时前
2025 蓝桥杯C/C++国B 部分题解
c语言·c++·笔记·学习·贪心算法·蓝桥杯
受之以蒙4 小时前
web-sys进阶:事件处理、异步操作与 Web API 实践
笔记·rust·webassembly
Olrookie6 小时前
若依前后端分离版学习笔记(七)—— Mybatis,分页,数据源的配置及使用
数据库·笔记·学习·mybatis·ruoyi