【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 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
汇能感知3 小时前
摄像头模块在运动相机中的特殊应用
经验分享·笔记·科技
阿巴Jun4 小时前
【数学】线性代数知识点总结
笔记·线性代数·矩阵
茯苓gao4 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾4 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT5 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa5 小时前
HTML和CSS学习
前端·css·学习·html
ST.J5 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
Suckerbin5 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全
看海天一色听风起雨落6 小时前
Python学习之装饰器
开发语言·python·学习