|
偷空消化了下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, ···])。内容可能有点绕,直接上代码。- print("coroutine start!");
- --没有yield的协程
- local newCor1 = coroutine.create(function()
- return 1,"a"
- end)
- local ret1, num1, str1 = coroutine.resume(newCor1)
- print("1-----", ret1, num1, str1)
- --包含一个yield的协程,主要看参数相关
- local newCor2 = coroutine.create(function(x)
- x = x+10;
- --str和y的值为resume传入的值
- local str, y = coroutine.yield(x);
- return str, y + x
- end)
- local ret2, x = coroutine.resume(newCor2, 50)
- print("2-----", x)
- local ret3, str2, y = coroutine.resume(newCor2, "sss", 100);
- print("2-----", str2, y)
复制代码 最终Log如下:
接着可以看看wrap的方式,具体的区别在于,wrap的返回值是一个函数,所以唤起协程的时候不需要调用resume方法,直接调用wrap返回的函数即可。还有就是和resume返回的不同的是,返回值中少了协程是否成功运行的布尔值。直接看代码吧:- local newCor3 = coroutine.wrap(function(x)
- x = x - 10;
- local y = coroutine.yield(x);
- return y;
- end)
- --不需要resume函数来唤起,直接调用wrap返回的值
- local ret4 = newCor3(100);
- print("3-----", ret4)
- local ret5 = newCor3(10);
- print("3-----", ret5)
复制代码 Log如下:
关于其他的API大家可以自己试试,简单愉快的。
xlua async_to_sync
在看demo的时候大家会发现,里面都调用了util里面的async_to_sync方法。有点不明所以,如果看函数名的话理解可以理解为异步变同步。后面看了下具体的函数实现,发现似乎并不是那么回事,简单的来说是将一个方法包装成一个需要在协程中执行的方法,并在原方法中添加一个回调函数的参数,原方法执行完即挂起这个协程,当原方法执行传入的回调函数之后,再次启动该协程,并最终执行完毕,返回传入回调的参数的值。
coroutine_call()方法就很简单,就是创建一个新的协程然后启动它。- local function async_to_sync(async_func, callback_pos)
- return function(...)
- --coroutine.running() 返回当前正在运行的协程加一个布尔量。 如果当前运行的协程是主线程,其为真。
- local _co = coroutine.running() or error ('this function must be run in coroutine')
- local rets
- local waiting = false
- --回调方法
- local function cb_func(...)
- if waiting then
- --再次启动协程
- assert(coroutine.resume(_co, ...))
- else
- rets = {...}
- end
- end
- local params = {...}
- --添加回调
- table.insert(params, callback_pos or (#params + 1), cb_func)
- --unpack逐个返回所有的表元素
- async_func(unpack(params))
- if rets == nil then
- waiting = true
- --协程挂起,当执行回调的时候再次启动,ret为cb_func传入的参数
- rets = {coroutine.yield()}
- end
- --返回传入回调的参数值
- return unpack(rets)
- end
- end
- local function coroutine_call(func)
- return function(...)
- local co = coroutine.create(func)
- assert(coroutine.resume(co, ...))
- end
- end
复制代码 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|