利用Lua協程實現Future模式

1. Future模式:

參見http://www.cnblogs.com/zhiranok/archive/2011/03/26/Future_Pattern.htmlhtml

使用future的好處是即利用了異步的並行能力,又保證主邏輯串行執行,保持簡單。mysql

2. Lua 協程

sina Timyang 的介紹 http://timyang.net/lua/lua-coroutine/c++

lua coroutine 經過create建立一個僞線程,該「線程」經過yield能夠掛起本身,經過調用resume可使該「線程」從掛起位置繼續執行。sql

3. Lua coroutine 實現 Future

假設有以下應用場景:緩存

1. 用戶登陸系統,須要將用戶數據從Mysql中獲取用戶數據,而後在Lua中實例化user_t對象。異步

2. 用戶登陸事件由C++觸發,將uid參數傳遞給luaasync

3. lua 並不存在mysql接口,必須委託c++完成mysql操做,並且Lua state必須被單線程操做,故咱們指望Lua不能被阻塞,在單個user從mysql 載入數據時其餘user應該可以繼續接受請求函數

咱們設計了以下解決方案:post

1. lua中的user_t對象每一個實例擁有兩個主要數據,

 a. request_cache,在user未初始化完成時該uid的請求將被緩存起來(咱們將請求封裝成function)。ui

    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 do
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請求會被單個協程串行化。

相關文章
相關標籤/搜索