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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM