• Lua CallbackHell优化


    概述

    在异步操作中,常常要使用回调。但是,回调的嵌套常常会导致逻辑混乱,一步错步步错,难以维护。在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()
    

    输出:
    image

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

    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)
    

    输出:
    image

    • 用AsyncFunc和BeginTask分别对异步函数和协程创建和运行做了封装
    • 注意:不能在主协程中运行AsyncFunc,用BeginTask开启一个新的协程运行Main
  • 相关阅读:
    数字孪生技术在物流领域的应用举例
    JavaWeb 项目 --- 博客系统(前后分离)
    C#,图论与图算法,图着色问题(Graph Coloring)的威尔士-鲍威尔(Welch Powell Algorithm)算法与源代码
    猫头虎分享从Python到JavaScript传参数:多面手的数据传递术
    CXYGZL实现钉钉、飞书和微信全面覆盖!!!
    matplotlib
    Flutter编写的数独游戏
    Word编辑论文,实现1.题目、摘要、关键词为通栏,正文为双栏 2.首页底端添加通栏脚注,在脚注中写作者简介,并使其实现悬挂对齐效果
    R语言绘制时间序列的偏自相关函数图:使用pacf函数可视化时间序列数据的偏自相关系数图、分析是否存在自相关性以及显著相关的个数
    pytorch使用cuda时遇到了如下问题
  • 原文地址:https://www.cnblogs.com/hggzhang/p/16684901.html