封装 go 函数
在 使用 Lua 协程处理异步回调函数 中已经介绍
这里简要列下:
-
封装 go 函数
lua---go 函数创建并启动一个协程 ---@param _co_task function @函数原型 fun(_co:thread) function go(_co_task) local co = coroutine.create(_co_task) -- 创建的协程是暂停的 coroutine.resume(co, co) -- 调用 coroutine.resume 激活协程执行 end
-
封装项目中异步函数
lua---封装 c_model.c_foo 异步函数,成为协程函数 ---@param _co thread @协程对象 ---@return boolean,string function co_foo(_co) c_model.c_foo(function(_ok, _result) coroutine.resume(_co, _ok, _result) -- 2. 回调函数被调用后,激活本协程继续执行。并把_ok, _result传递给 yield end) return coroutine.yield() -- 1. 主动放弃运行,本协程被切换出去 end
-
使用例子
lua---test顺序编写代码,解决回调函数造成同块逻辑被撕裂的例子 ---@param _co thread @协程对象 function test(_co) for i = 1, 10, 1 do local ok, result = co_foo(_co) -- co_foo 会先 yield 切出;内部回调被执行时, resume 重新切回来继续执行 print(ok, result) end end -- 启动 test 协程 go(test)
封装 defer
defer 的特点有以下:
- 协程正常退出能被执行
- 协程异常退出能被执行
- 同个协程内可以多次调用 defer
- defer 被执行时,按出栈顺序被执行
defer 多次执行
首先定义 defer 函数,让它具备能多次被调用:
lua
function defer(_co_wrap, h)
table.insert(_co_wrap.defer_handlers, h)
end
因为要对 defer 的函数句柄做保持,以便退出时执行。包裹了下 co 对象:
lua
---@class co_wrap
---@field co thread
---@field defer_handlers fun(_co_error:co_error)[]
同时定义下让 defer 的函数知道是否有错误的对象:
lua
---@class co_error
---@field ok boolean
defer 被执行时,按出栈顺序被执行
lua
function invoke_defer_handlers(_co_wrap, _co_error)
for i=#_co_wrap.defer_handlers, 1, -1 do
local h = _co_wrap.defer_handlers[i]
xpcall(h, function(err) print(err) end, _co_error)
end
end
协程异常时,能被执行
Lua 协程异常,通过 coroutine.resume
捕获,并返回错误信息
因此主要封装下 coroutine.resume
:
lua
function coroutine_resume(_co_wrap, ...)
local ok, errmsg = coroutine.resume(_co_wrap.co, ...)
if not ok then
invoke_defer_handlers(_co_wrap, {ok=false}) -- 异常退出
end
end
协程正常退出时,能被执行
lua
function go(_co_task)
local co = coroutine.create(function(_co_wrap)
_co_task(_co_wrap)
invoke_defer_handlers(_co_wrap, {ok=true}) -- 正常退出
end)
local cowrap = { co = co, defer_handlers = {} } ---@type co_wrap
coroutine_resume(cowrap, cowrap) -- 初创建的协程是暂停的,手动触发执行
end
以上就可以在 Lua 中完全 Golang 的方式编写协程代码了
协程间通信
由于项目中暂时是一根线程管理一个 lua_state 对象,因此暂时无需求多线程中的协程间的通信需求
待续