Unity XLua 协程 Coroutine

版权声明:辛辛苦苦写的,转载请注明出处噢 https://blog.csdn.net/wangjiangrong/article/details/79886470

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



阅读更多

没有更多推荐了,返回首页