• lua table vs closure


      最近在重构自己写的框架中的定时器模块,需要把回调函数保存起来,大概如下:

    function timer_mgr:save_timer( this,callback )
        return { this = this,callback = callback}
    end
    
    -- 创建新定时器
    -- @after:延迟N秒后启动定时器
    -- @repeated:N秒循环
    -- @this:回调对象
    -- @callbakck:回调函数
    function timer_mgr:new_timer( after,repeated,this,callback )
        local timer_id = self:next_id()
        self.timers[timer_id] = save_timer( this,callback )
    end
    
    function timer_mgr:do_timer( timer_id )
        local timer = self.timers[timer_id]
    
        -- 调用之前保存的定时器回调
        return timer.callback( timer.this )
    end

    正常情况下,用table保存定时器的回调参数,毕竟lua中也没有太多的数据结构可以选择。不过,我们也可以这样用closure来保存:

    function timer_mgr:save_timer( this,callback )
        return function()
            return callback( this )
        end
    end
    
    -- 创建新定时器
    -- @after:延迟N秒后启动定时器
    -- @repeated:N秒循环
    -- @this:回调对象
    -- @callbakck:回调函数
    function timer_mgr:new_timer( after,repeated,this,callback )
        local timer_id = self:next_id()
        self.timers[timer_id] = save_timer( this,callback )
    end
    
    function timer_mgr:do_timer( timer_id )
        local timer = self.timers[timer_id]
    
        -- 调用之前保存的定时器回调
        return timer()
    end

    这样似乎看起来更优雅更方便一些,不过,频繁创建closure也是很消耗内存和cpu的,需要和table对比一下:

    function test_table_object( this,cb )
        return { this = this,cb = cb }
    end
    
    function test_closure_object( this,cb )
        return function()
            return cb( this )
        end
    end
    
    local function cb()
        print("do nothing ...")
    end
    
    local max = 1000000
    
    local table_mgr = {}
    local closure_mgr = {}
    
    function test_table()
        
        local beg_m = collectgarbage("count")
        local beg_tm = os.clock()
    
        for idx = 1,max do
            table_mgr[idx] = test_table_object( {},cb )
        end
    
        local end_m = collectgarbage("count")
        local end_tm = os.clock()
    
        print("table test",end_m - beg_m,end_tm - beg_tm)
    end
    
    function test_closure()
        
        local beg_m = collectgarbage("count")
        local beg_tm = os.clock()
    
        for idx = 1,max do
            table_mgr[idx] = test_closure_object( {},cb )
        end
    
        local end_m = collectgarbage("count")
        local end_tm = os.clock()
    
        print("closure test",end_m - beg_m,end_tm - beg_tm)
    end
    
    collectgarbage("stop")
    collectgarbage("collect")
    
    test_closure()
    test_table()

    这段代码,分别在win10 - I3 cpu和debian7(虚拟机) - A8 cpu下测试:

    closure test	117946.5	0.489
    table test	125000.0	0.446
    
    closure test	180446.5	0.75
    table test	171875.0	0.72

    可以看到,table和closure的消耗其实都差不多。但closure在这个应用场景下就优雅得多,因为可以很方便地传更多的参数,比如:

    function timer_mgr:save_timer( this,callback,... )
        return function()
            return callback( this,... )
        end
    end
    
    function timer_mgr:new_timer( after,repeated,this,callback,... )
        local timer_id = self:next_id()
        self.timers[timer_id] = save_timer( this,callback,... )
    end
    
    function timer_mgr:do_timer( timer_id )
        local timer = self.timers[timer_id]
    
        -- 调用之前保存的定时器回调
        return timer()
    end

    而table要传可变参则不太好处理了,需要另外创建一个table来pack参数,调用时再unpack,或者只用一个table把callback函数和参数存一起,调用时把函数移除再unpack。总而言之,在这种需要保存参数的回调,closure更合适。当然,如果你的回调函数不带参数,那就是另外一码事了。

      查了下,之前也有人做了类似的测试,结果也差不多:http://lua-users.org/wiki/ObjectOrientationClosureApproach

  • 相关阅读:
    阅读笔记十四
    惨淡的蓝桥杯国赛经历
    阅读笔记十三
    阅读笔记十二
    阅读笔记十一
    阅读笔记十
    阅读笔记九
    阅读笔记八
    阅读笔记七
    阅读笔记六
  • 原文地址:https://www.cnblogs.com/coding-my-life/p/10923988.html
Copyright © 2020-2023  润新知