当前位置:   article > 正文

Unity XLua 协程 Coroutine_attempt to yield from outside a coroutine

attempt to yield from outside a coroutine

偷空消化了下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



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/95851
推荐阅读
相关标签
  

闽ICP备14008679号