概述

在异步操作中,常常要使用回调。但是,回调的嵌套常常会导致逻辑混乱,一步错步步错,难以维护。在Lua中,可以使用协程进行优化。

问题分析

模拟一个回合制游戏攻击过程

local function PlayAnim(anim, cb)
print("开始播放 " .. anim)
os.execute("sleep " .. 1)
print("播放完成 " .. anim)
cb()
end local function Main()
print("行动开始")
PlayAnim("移动到目标动画",function()
print("开始攻击")
PlayAnim("攻击动画",function()
print("返回到原位置")
PlayAnim("返回动画",function()
print("行动结束")
end)
end)
end)
end Main()

输出:

  • 可以看到异步回调的嵌套导致代码结构混乱

Lua协程

简介

  • Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
  • 线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
  • 协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
  • coroutine在底层实现就是一个线程。

API

API 说明
coroutine.create() 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
coroutine.resume() 重启 coroutine,和 create 配合使用
coroutine.yield() 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果
coroutine.status() 查看 coroutine 的状态。注:coroutine 的状态有三种:dead,suspended,running
coroutine.wrap() 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复
coroutine.running() 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号

详细介绍

请参考跳转链接:菜鸟教程-协程

解决方案

function AsyncFunc(func)
return function(...)
local current_co = coroutine.running()
local ret, res = false, nil
local function cb(...)
if not coroutine.resume(current_co, ...) then
ret = true
res = ...
end
end
local params = table.pack(...)
table.insert(params, cb)
func(table.unpack(params))
if not ret then
res = coroutine.yield()
end
return res
end
end function BeginTask(func, ...)
local t = coroutine.create(func)
coroutine.resume(t, ...)
end local function PlayAnim(anim, cb)
print("开始播放 " .. anim)
os.execute("sleep " .. 1)
print("播放完成 " .. anim)
cb()
end local AsyncPlayAnim = AsyncFunc(PlayAnim)
local function Main()
print("行动开始")
AsyncPlayAnim("移动到目标动画")
print("开始攻击")
AsyncPlayAnim("攻击动画")
print("返回到原位置")
AsyncPlayAnim("返回动画")
print("行动结束")
end BeginTask(Main)

输出:

  • 用AsyncFunc和BeginTask分别对异步函数和协程创建和运行做了封装
  • 注意:不能在主协程中运行AsyncFunc,用BeginTask开启一个新的协程运行Main

最新文章

  1. go:多核并行化问题
  2. linux下gimp的使用
  3. [cocos2d-x]OPENGL ES支持的像素格式
  4. Java构造方法的含义和使用
  5. servlet中避免405错误的产生
  6. 正式生产环境下hadoop集群的DNS+NFS+ssh免password登陆配置
  7. linux 非root用户 ssh 免密码登录
  8. 说声PHP的setter&getter(魔术)方法,你们辛苦了
  9. matplotlib 中的柱状图
  10. [转载]C# 常用日期时间函数(老用不熟)
  11. Kaggle(1):数据挖掘的基本流程
  12. jquery中使用datepicker限制开始日期小于结束日期
  13. Eclipse 使用 SVN 插件后修改用户方法汇总
  14. C++入门学习
  15. iperf3.0 hisi uclib 交叉编译
  16. int最大值+1为什么是-2147483648最小值-1为什么是2147483647
  17. CSS之inline和inline-block
  18. 使用 input[type=file]上传文件
  19. flask第三方插件WTForms
  20. Spark-Dependency

热门文章

  1. Python+opencv打开修图的正确方式get
  2. docker安装node
  3. appium简单使用
  4. 常用的Linux命令和Git的必要配置
  5. GitLab:Your account has been blocked.
  6. 2022-7-21 第七组 pan小堂 继承与super与this
  7. 整除分块套杜教筛为什么是 O(n^2/3) 的
  8. Java开发学习(十九)----AOP环绕通知案例之密码数据兼容处理
  9. Linux系列之管理用户环境变量
  10. mysql like 命中索引