找回密码
 立即注册
查看: 388|回复: 1

Unity XLua 协程 Coroutine

[复制链接]
发表于 2021-8-12 17:45 | 显示全部楼层 |阅读模式
偷空消化了下XLua的官方demo,在协程相关的demo里面(06_Coroutine和07_AsyncTest)感觉有些知识点可以记录一下。其中还涉及到util.lua里的几个方法。
同时有了第一个小粉丝,感觉还是很美滋滋的,说明记录的一些内容还是阔以的


Lua5.3 API:http://www.runoob.com/manual/lua53doc/manual.html
简介

首先讲讲Lua中的协程概念以及用法,其实API里面已经讲得很详细了,这边就简单的说一下。
Lua 中一个协程在 Lua 中代表了一段独立的执行线程。 然而,与多线程系统中的线程的区别在于, 协程仅在显式调用一个让出(coroutine.yield())函数时才挂起当前的执行。注:运行的是主线程时调用coroutine.yield()会报错LuaException: attempt to yield from outside a coroutine



API

coroutine.create (f)

创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回这个新协程,它是一个类型为 "thread" 的对象。
coroutine.isyieldable ()

如果正在运行的协程可以让出,则返回真。
不在主线程中或不在一个无法让出的 C 函数中时,当前协程是可让出的。
coroutine.resume (co [, val1, ···])

开始或继续协程 co 的运行。 当你第一次延续一个协程,它会从主体函数处开始运行。 val1, ... 这些值会以参数形式传入主体函数。 如果该协程被让出,resume 会重新启动它; val1, ... 这些参数会作为让出点的返回值。
如果协程运行起来没有错误, resume 返回 true 加上传给 yield 的所有值 (当协程让出), 或是主体函数的所有返回值(当协程中止)。 如果有任何错误发生, resume 返回 false 加错误消息。
coroutine.running ()

返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
coroutine.status (co)

以字符串形式返回协程 co 的状态: 当协程正在运行(它就是调用 status 的那个) ,返回 "running"; 如果协程调用 yield 挂起或是还没有开始运行,返回 "suspended"; 如果协程是活动的,都并不在运行(即它正在延续其它协程),返回 "normal"; 如果协程运行完主体函数或因错误停止,返回 "dead"。
coroutine.wrap (f)

创建一个主体函数为 f 的新协程。 f 必须是一个 Lua 的函数。 返回一个函数, 每次调用该函数都会延续该协程。 传给这个函数的参数都会作为 resume 的额外参数。 和 resume 返回相同的值, 只是没有第一个布尔量。 如果发生任何错误,抛出这个错误。
coroutine.yield (···)

挂起正在调用的协程的执行。 传递给 yield 的参数都会转为 resume 的额外返回值。

使用

首先自然是要创建一个新的协程。根据上面的API我们知道有两种方法可以创建一个新的协程create和wrap。先讲讲create的方式,我们先用local newCor = coroutine.create(function),建立一个新的协程。这个时候这个协程并不会运行,即function函数不会执行。我们还需要调用local ret1[, ret2, ···] = coroutine.resume (newCor [, val1, ···]),第一个参数即为create的返回值,后面的可变参数将作为协程主函数的参数传入主函数中,即作为上面create里面参数function的参数。这个时候协程newCor就开始运行,当遇到主函数中的第一个local newVal1[, newVal2, ···] = coroutine.yield(ret2[, ret3, ···]);,将会挂起协程。这时上面resume的返回值ret将为true 和yield的返回值。若函数体没有yield,ret的值为true和函数体内所有的返回值。若协程运行出错,则ret的返回值为false和错误信息。当协程被挂起后,我们可以再次用resume去唤起,这时候resume里传入的参数,将作为yield的返回值给到函数体内(即上面的newVal1[, newVal2, ···])。内容可能有点绕,直接上代码。
  1. print("coroutine start!");
  2. --没有yield的协程
  3. local newCor1 = coroutine.create(function()
  4.         return 1,"a"
  5. end)
  6. local ret1, num1, str1 = coroutine.resume(newCor1)
  7. print("1-----", ret1, num1, str1)
  8. --包含一个yield的协程,主要看参数相关
  9. local newCor2 = coroutine.create(function(x)
  10.         x = x+10;
  11.         --str和y的值为resume传入的值
  12.         local str, y = coroutine.yield(x);
  13.         return str, y + x
  14. end)
  15. local ret2, x = coroutine.resume(newCor2, 50)
  16. print("2-----", x)
  17. local ret3, str2, y = coroutine.resume(newCor2, "sss", 100);
  18. print("2-----", str2, y)
复制代码
最终Log如下:


接着可以看看wrap的方式,具体的区别在于,wrap的返回值是一个函数,所以唤起协程的时候不需要调用resume方法,直接调用wrap返回的函数即可。还有就是和resume返回的不同的是,返回值中少了协程是否成功运行的布尔值。直接看代码吧:
  1. local newCor3 = coroutine.wrap(function(x)
  2.         x = x - 10;
  3.         local y = coroutine.yield(x);
  4.         return y;
  5. end)
  6. --不需要resume函数来唤起,直接调用wrap返回的值
  7. local ret4 = newCor3(100);
  8. print("3-----", ret4)
  9. local ret5 = newCor3(10);
  10. print("3-----", ret5)
复制代码
Log如下:


关于其他的API大家可以自己试试,简单愉快的。

xlua async_to_sync

在看demo的时候大家会发现,里面都调用了util里面的async_to_sync方法。有点不明所以,如果看函数名的话理解可以理解为异步变同步。后面看了下具体的函数实现,发现似乎并不是那么回事,简单的来说是将一个方法包装成一个需要在协程中执行的方法,并在原方法中添加一个回调函数的参数,原方法执行完即挂起这个协程,当原方法执行传入的回调函数之后,再次启动该协程,并最终执行完毕,返回传入回调的参数的值。
coroutine_call()方法就很简单,就是创建一个新的协程然后启动它。
  1. local function async_to_sync(async_func, callback_pos)
  2.     return function(...)
  3.                 --coroutine.running() 返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
  4.         local _co = coroutine.running() or error ('this function must be run in coroutine')
  5.         local rets
  6.         local waiting = false
  7.                 --回调方法
  8.         local function cb_func(...)
  9.             if waiting then
  10.                                 --再次启动协程
  11.                 assert(coroutine.resume(_co, ...))
  12.             else
  13.                 rets = {...}
  14.             end
  15.         end
  16.         local params = {...}
  17.                 --添加回调
  18.         table.insert(params, callback_pos or (#params + 1), cb_func)
  19.                 --unpack逐个返回所有的表元素
  20.         async_func(unpack(params))
  21.         if rets == nil then
  22.             waiting = true
  23.                         --协程挂起,当执行回调的时候再次启动,ret为cb_func传入的参数
  24.             rets = {coroutine.yield()}
  25.         end
  26.                 --返回传入回调的参数值
  27.         return unpack(rets)
  28.     end
  29. end
  30. local function coroutine_call(func)
  31.     return function(...)
  32.         local co = coroutine.create(func)
  33.         assert(coroutine.resume(co, ...))
  34.     end
  35. end
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-8-25 14:43 | 显示全部楼层
感谢,感谢,帮到了很多,学到了
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-6-1 11:30 , Processed in 0.128225 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表