利用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:

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请求会被单个协程串行化。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员维他命

YYCache 源码解析(二):磁盘缓存的设计与缓存组件设计思路

上一篇讲解了YYCache的使用方法,架构与内存缓存的设计。这一篇讲解磁盘缓存的设计与缓存组件的设计思路。

1202
来自专栏数据之美

Python FAQ(常见问题解答)(1)

声明:转载需署名出处,严禁用于商业用途! 1、python的帮助: help(str) 可以查看str字符类的帮助信息。 2、python没有括号来表明...

1818
来自专栏大学生计算机视觉学习DeepLearning

c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型 多线程超详细教程 以及 多线程实现服务端

原文链接:https://www.cnblogs.com/DOMLX/p/9661012.html

592
来自专栏Golang语言社区

Golang语言学习-并发

goroutine: 由GO运行环境管理的轻量级线程 channel: 有类型的管道,操作符为 <- 数据流向箭头指向的方向 使用make(chan ...

3458
来自专栏JAVA高级架构

JAVA后端面试100 Q&amp;A之第一篇

911
来自专栏Golang语言社区

Golang同步:锁的使用案例详解

互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段。它由标准库代码包sync中的Mutex结构体类型代表。只有两个公开方法 Lock Unlock ...

3447
来自专栏计算机视觉与深度学习基础

Leetcode 80 Remove Duplicates from Sorted Array II

Follow up for "Remove Duplicates": What if duplicates are allowed at most twic...

1749
来自专栏MasiMaro 的技术博文

驱动程序的同步处理

驱动程序运行在系统的内核地址空间,而所有进程共享这2GB的虚拟地址空间,所以绝大多数驱动程序是运行在多线程环境中,有的时候需要对程序进行同步处理,使某些操作是严...

751
来自专栏Java进阶

volatile和synchronized 实现原理的差别

2967
来自专栏Create Sun

python 3.x 与2.x的区别

前言   保持学习的态度,学一门动态语言其实是很早以前的就准备要做的事情,当时还在纠结python与ruby。现在不单单是要学python,还在考虑用它做点什么...

3758

扫码关注云+社区