zoukankan      html  css  js  c++  java
  • 利用LUA协程实现FUTURE模式

    1. Future模式:

    参见http://www.cnblogs.com/zhiranok/archive/2011/03/26/Future_Pattern.html

    使用future的好处是即利用了异步的并行能力,又保证主逻辑串行执行,保持简单。

    2. Lua 协程

    sina Timyang 的介绍 http://timyang.net/lua/lua-coroutine/

    lua coroutine 通过create创建一个伪线程,该“线程”通过yield可以挂起自己,通过调用resume可以使该“线程”从挂起位置继续执行。

    3. LUA coroutine 实现 Future

    假设有如下应用场景:

    1. 用户登录系统,需要将用户数据从Mysql中获取用户数据,然后在LUA中实例化user_t对象。

    2. 用户登录事件由C++触发,将uid参数传递给lua

    3. lua 并不存在mysql接口,必须委托c++完成mysql操作,而且lua state必须被单线程操作,顾我们期望LUA不能被阻塞,在单个user从mysql 载入数据

      时其他user应该能够继续接受请求

    故我们设计了如下解决方案:

    1. lua中的user_t对象每个实例拥有两个主要数据,

      a. request_cache,在user未初始化完成时该uid的请求将被缓存起来(我们将请求封装成function)。

          b. coroutine ,该协程尝试将request_cache中的所有请求执行完毕,当出现如下情况该协程为挂起自己

        (1)request_cache 为空,挂起等待新的请求

        (2)需要执行mysql时挂起,等待mysql执行完毕被唤醒。

    示例代码:

         

     1 user_t = {}
    2 user_t.__index = user_t
    3
    4 function user_t:new()
    5 local funjc = function() print("TODO exe all request in request_cache") end
    6 local ret =
    7 {
    8 ["request_cache"] = {},
    9 ["coroutine_obj"] = coroutine.create(funjc),
    10 }
    11 setmetatable(ret, self)
    12 return ret
    13 end

    2. C++ 封装异步调用Mysql的接口,注册接口到LUA

    1. future_t 用于LUA和C++传递数据

    1 class future_t
    2 {
    3 public:
        void   set_result(const string& v_) { m_result = v_;   }
    4 string get_result() const { return m_result; }
    5 private:
    6 string m_result;
    7 };

    2. async_load_data_from_db 用于异步执行mysql操作

     1 void async_load_data_from_db(future_t* ret_)
    2 {
    3 //! post another thread, async exe load data from db
    4 thread.post(boost::bind(do_load_data_from_db, ret_));
    5 }
    6
    7 void do_load_data_from_db(future_t* ret_)
    8 {
    9 //! TODO exe sql opertion
    10 lua_pcall("resume_routine")
    11 }
    lua 调用C++的接口async_load_data_from_db,async_load_data_from_db 将请求post另外的线程,执行mysql请求,将请求结果赋值到future中,调用lua的resume函数唤醒
    lua协程继续执行

    3. LUA 示例代码

     1 user_t = {}
    2 user_t.__index = user_t
    3
    4 function user_t:new(uid_)
    5 local ret =
    6 {
    7 ["uid"] = uid_,
    8 ["request_cache"] = {},
    9 ["coroutine_obj"] = true,
    10 ["runing_flag"] = true,
    11 }
    12 setmetatable(ret, self)
    13
    14 local func = function()
    15 while true == runing_flag
    16 if 0 == #ret.request_cache
    17 then
    18 coroutine.yield()
    19 else
    20 local todo_func = ret.request_cache[1]
    21 local tmp = {}
    22 for k = 2, #ret.request_cache
    23 do
    24 table.insert(tmp, ret.request_cache[k])
    25 end
    26 ret.request_cache = tmp
    27 todo_func()
    28 end
    29 end
    30 end
    31 ret.coroutine_obj = coroutine.create(func)
    32 return ret
    33 end
    34
    35 function user_t:init()
    36 local func = function()
    37 local future = future_t:new()
    38 async_load_data_from_db(future)
    39 coroutine.yield()
    40 print("user_t:init ok", self.uid, future:get_result())
    41 future:delete()
    42 end
    43 table.insert(self.request_cache, func)
    44 coroutine.resume(self.coroutine_obj)
    45 end
    46
    47 function user_t:resume_routine()
    48 coroutine.resume(self.coroutine_obj)
    49 end
    50
    51 local test_user = user_t:new(1122334)
    52
    53 function user_login()
    54 return test_user:init()
    55 end
    56
    57 function resume_routine()
    58 return test_user:resume_routine()
    59 end

    4. 注意事项:

    尽管一个lua state是串行执行的,使用lua coroutine时仍然要注意数据一致性,比如在coroutine执行时使用了全局变量,yield挂起后全局变量有可能被修改了,

    所以协程适合于例子中的user_t对象,各个user是互不干扰的,相同的user请求会被单个协程串行化。

  • 相关阅读:
    Linux菜鸟级重点
    在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误
    搭建PHP开发环境
    Struts+Hibernate+Spring实现用户登录功能
    Struts2整合Hibernate3实现用户登录功能
    决战JS(二)
    决战JS
    lightoj-1098
    lightoj-1072
    lightoj-1094 Farthest Nodes in a Tree(求树的直径)
  • 原文地址:https://www.cnblogs.com/zhiranok/p/lua_coroutine_future.html
Copyright © 2011-2022 走看看