免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: csumck
打印 上一主题 下一主题

[技术动态] 今天听说了腾讯一个很牛逼的技术, 求问是怎么实现的 [复制链接]

论坛徽章:
0
41 [报告]
发表于 2014-11-12 19:04 |只看该作者
不就是协程么。

楼上有个喷lua的请自重,lua是非对称协程,但不代表是阻塞的。

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
42 [报告]
发表于 2015-04-30 19:55 |只看该作者
请参考
Async MySQL Queries with C-API
http    jan.kneschke.de/2008/9/9/async-mysql-queries-with-c-api

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
43 [报告]
发表于 2015-05-12 02:13 |只看该作者
回复 30# Fixend


    怎么会呢?Lua的协程是异常强大的,我就举个简单的例子,假设你想递归地访问一个表,通常的写法是这样的:

  1. local function trace(t, f)
  2.    for i, v in ipairs(t) do
  3.       if type(v) == "table" then
  4.          trace(v, f)
  5.       else
  6.          f(v)
  7.       end
  8.    end
  9. end

  10. trace({1,2,3,{4,5,{6,7,8}},9,{10}}, print)
复制代码
可是,如果你打算一次取一个值,去做一些很复杂的操作(特别的,会涉及到上下文的操作),比如说,求一下这些值的和,比较简单的做法就是闭包加upvalue:

  1. local count = 0
  2. trace({......}, function() count = count + 1 end)
复制代码
问题是,如果我这里的处理流程就要求我,这个地方能一个一个地取值怎么办?

C/C++的办法是写一个迭代器,自己模拟栈,这很麻烦,而且需要技巧,但是Lua里面简单到爆了:

  1. local iter = coroutine.wrap(function(t) trace(t, coroutine.yield) end)
  2. for v in iter,t do
  3.    print(v)
  4. end
复制代码
其实非常简单,coroutine.wrap将一个函数转换成协程,每次调用都会resume,而回调只需要简单地设置为yield即可。

这就是协程改变操作流程的强大所在,它能在用代码表示的,复杂的操作流程中插入tag,然后在其他地方去监听这些tag,从而把一个复杂的递归过程变成线性的。

实际的应用,可以考虑看看我的一个Lua扩展模块,luasched:https://github.com/starwing/luasched

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
44 [报告]
发表于 2015-05-12 02:19 |只看该作者
回复 28# cokeboL


    擦,上面的帖子是回复你的,我回复错人了!!

恩,不管了,既然回错了,我就顺便举个例子吧。比如说,回调模式,我想买五个道具:

function buy(item, callback) ... end

怎么办?

  1. local function buy_times(n)
  2.   buy(item, function()
  3.     if n > 1 then
  4.         buy_times(n-1)
  5.     end
  6.   end)
  7. end
复制代码
一个超级简单的需求就这样成了递归函数了= =
那么,如果用协程呢?

假设我有个函数叫buyCO,他不是接受回调,而是在内部yield,那么我大可以这么写:

  1. task.new(function(n)
  2.    for i = 1, n do
  3.       buyCO(item)
  4.    end
  5. end)
复制代码
就这样就可以了,task会保证这个函数会在“适当的时候”执行。简单吧?

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
45 [报告]
发表于 2015-05-12 09:11 |只看该作者
回复 44# starwing83
  1. local iter = coroutine.wrap(function(t) trace(t, coroutine.yield) end)
  2. for v in iter,tab do
  3.    print(v)
  4. end

  5. trace(tab, function(v)
  6.         print(v)
  7. end)
复制代码
这两种写法,我个人觉得下面的更简洁,逻辑也比较直接,trace内部也是一个一个callback
用coroutine多写了点看上去很屌的代码,但看上去未必直接,关键是,yield给其他地方再回来,跟回调去调用别个再回来没本质区别,
而且单线程的,并没有带来异步的好处,回调反倒可以在c/c++层做异步的事情来提高效率

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
46 [报告]
发表于 2015-05-12 09:13 |只看该作者
回复 41# xphh2008


    我说阻塞的意思是,lua为单线程,yield之后其实跟调用其他函数等调用完再回来没本质区别,都是上厕所排队,没有带来异步的好处,参考楼上帖

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
47 [报告]
发表于 2015-05-12 16:35 |只看该作者
本帖最后由 yulihua49 于 2015-05-12 16:39 编辑
cokeboL 发表于 2015-05-12 09:13
回复 41# xphh2008

如果是单线程,是否yield之后,线程可以给别的客户端服务了?
如果是在多客户端的服务器里。

没弄明白,lua是怎么设定resume的条件的?

在C里边,都是在yield之前先准备好resume的条件。

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
48 [报告]
发表于 2015-05-12 17:12 |只看该作者
回复 47# yulihua49


    不知道。。。最初开始学lua看到协程的时候,我就个人判定lua的协程是个鸡肋的东西,再没深入研究

   

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
49 [报告]
发表于 2015-05-12 18:58 |只看该作者
回复 47# yulihua49

就是那个iter,coroutine写iter是yield,外面读iter是resume

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
50 [报告]
发表于 2015-05-12 22:24 |只看该作者
回复 47# yulihua49


golang这种语言级实现的并发协程才好用,不需要去做任务线程、队列、回调之类的

传统的进程池、线程池调度开销大,goroutine开销小得多,就可以搞成池,比如
每个连接一个goroutine,然后1对1的的处理,可以写线性代码了

随便搜了两段:

  1. //server.go

  2. package main

  3. import (
  4.         "fmt"
  5.         "net"
  6. )

  7. const (
  8.         //绑定IP地址
  9.         ip = "127.0.0.1"
  10.         //绑定端口号
  11.         port = 3333
  12. )

  13. func main() {
  14.         listen, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(ip), port, ""})
  15.         if err != nil {
  16.                 fmt.Println("监听端口失败:", err.Error())
  17.                 return
  18.         }
  19.         fmt.Println("已初始化连接,等待客户端连接...")
  20.         Server(listen)
  21. }

  22. func Server(listen *net.TCPListener) {
  23.         for {
  24.                 conn, err := listen.AcceptTCP()
  25.                 if err != nil {
  26.                         fmt.Println("接受客户端连接异常:", err.Error())
  27.                         continue
  28.                 }
  29.                 fmt.Println("客户端连接来自:", conn.RemoteAddr().String())
  30.                 defer conn.Close()
  31.                 go func() {
  32.                         data := make([]byte, 10)
  33.                         for {
  34.                                 i, err := conn.Read(data)
  35.                                 fmt.Println("客户端发来数据:", string(data[0:i]))
  36.                                 if err != nil {
  37.                                         fmt.Println("读取客户端数据错误:", err.Error())
  38.                                         break
  39.                                 }
  40.                                 if string(data[:i]) == "exit" {
  41.                                         conn.Write([]byte{'e', 'x', 'i', 't'})
  42.                                         conn.Close()
  43.                                         break
  44.                                 }
  45.                                 conn.Write(data[0:i])
  46.                         }
  47.                 }()
  48.         }
  49. }
复制代码
  1. //client.go

  2. package main

  3. import (
  4.         "fmt"
  5.         "net"
  6. )

  7. const (
  8.         addr = "127.0.0.1:3333"
  9. )

  10. func main() {
  11.         conn, err := net.Dial("tcp", addr)
  12.         if err != nil {
  13.                 fmt.Println("连接服务端失败:", err.Error())
  14.                 return
  15.         }
  16.         fmt.Println("已连接服务器")
  17.         defer conn.Close()
  18.         Client(conn)
  19. }

  20. func Client(conn net.Conn) {
  21.         sms := make([]byte, 1024)
  22.         for {
  23.                 fmt.Print("请输入要发送的消息:")
  24.                 _, err := fmt.Scan(&sms)
  25.                 if err != nil {
  26.                         fmt.Println("数据输入异常:", err.Error())
  27.                 }
  28.                 conn.Write(sms)
  29.                 buf := make([]byte, 1024)
  30.                 c, err := conn.Read(buf)
  31.                 if err != nil {
  32.                         fmt.Println("读取服务器数据异常:", err.Error())
  33.                 }
  34.                 fmt.Println(string(buf[0:c]))
  35.         }

  36. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP