热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:合宙Luat|看《射雕英雄传》,聊聊LuaTask延时那些事

篇首语:本文由编程笔记#小编为大家整理,主要介绍了合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事相关的知识,希望对你有一定的参考价值。 武侠小说中,主人公之所以能纵

篇首语:本文由编程笔记#小编为大家整理,主要介绍了合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事相关的知识,希望对你有一定的参考价值。







武侠小说中,主人公之所以能纵横江湖,常常离不开一样可遇不可求的绝世法宝——武功秘籍。如今勇于尝试的开发者,笃定地告诉后来者:选Luat二次开发,就如同拥有了物联网开发的武功秘籍。


本期让我们通过《射雕英雄传》的一些小场景,聊聊LuaTask延时那些事儿~



不了解Luat开发的朋友,可参考学习:



http://doc.openluat.com/article/1719/0




http://doc.openluat.com/wiki/3?wiki_page_id=606
















1







什么是协程?












首先我们来看下什么是协程:


提起协程的话,大多数时候都会跟多线程进行比较。两者之间是有些相似的地方,都是程序上下文切换执行,都有并发性,但更多的还是区别所在。


多线程——是发生在系统态的程序切换,可以模拟较为真实的并发性。因为本身执行顺序带有一定的随机性,不可预测,所以很容易因为竞争造成数据混乱。


协程——是发生在用户态上的程序切换,只是对多线程的一种模拟,并不能替代多线程。它把本该在一个地方实现的代码拆分到了不同任务,让逻辑表述看起来更加清晰。





合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事



在《射雕英雄传》第十五章神龙摆尾中,有关黄蓉与欧阳克的比试有这样一段描写:






黄蓉走进圈子,道:“咱们是文打还是武打?”


欧阳克心道:“偏你就有这许多古怪。”问道:“文打怎样?武打怎样?”


黄蓉道:“文打是我发三招,你不许还手;你还三招,我也不许还手。武打是乱打一气,你用死蛇拳也好,活耗子拳也好,都是谁先出圈子谁输。” 












这个比试所定的规则,跟我们说的协程和多线程的关系是比较像的:


协程就是文打,执行权在两人之间交替运行,同一时间只能有一人挥拳,A打完之后B才能打。


多线程就是武打,两个人可以同时挥拳,互不影响。


如果用程序表示协程的话,代码可能会像下面这样:










Luat协程使用示例




























▼上下滚动,查看全部▼




function A()
   for i=1, 5 do

       coroutine.resume(co)  
       print("A\t"..i)

   end 

end


function B()

   for i=1, 5 do

       coroutine.yield()    

       print("B\t"..i)   

 end

end


co = coroutine.create(B)

A()

--[[ 

   A 1
   B 1
   A 2
   B 2
   A 3
   B 3
   A 4
   B 4
   A 5
]]





















这是一种简单的使用场景,yield和resume只是负责切换控制,让控制权在两个任务之间来回切换,达到了使两个任务 “并行” 的目的。


























2







协程的延时















合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


协程的概念说完了,但是...这跟延时有什么关系? 


其实稍加思索的话,就会发现这个黄蓉与欧阳克约定的文打规则里有个BUG: 


黄蓉先打三拳,然后欧阳克还三招,但是如果黄蓉只打了两拳就停手了,欧阳克怎么办? 


这就反映出协程中一个很现实的问题:


各个协程之间的延时是会相互影响的,你打了一拳去睡觉了,那我也必须跟着等,我回一拳之后也去睡觉了,你也必须跟着等。 


 如果反映到程序上,代码就是这样:
















▼上下滚动,查看全部▼




function wait(ms)

  -- 阻塞延时, 仅仅只做说明, 并不实现

end


function A()

  for i=1, 5 do

       coroutine.resume(co)    

       print("A\t"..i)

      wait(1000)   

   end 

end


function B()

  for i=1, 5 do

       coroutine.yield()   

       print("B\t"..i)

       wait(1000)   

    end

end


co = coroutine.create(B)

A()




















我们期望两个协程都能以1000ms的延时打印输出,但是这种阻塞延时其实是会在两个协程之间相互影响的:



A 在延时的过程中其实是会加长 B 的延时,最终两个协程都会以2000ms的延时打印输出。



这当然是一件很不合理的情况,我们希望代码中的程序段都应该保持一定的独立性,不能在修改一段代码之后莫名其妙的影响了另一段代码的执行情况。


























3







概念的偏差














合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


人们常说,比喻都是跛脚的。

在学习一些概念的时候,我们常常会借助一些比喻描述,加深理解。但是例子并不能完全做到等同,仅仅只能帮助理解某一面特性而已。


如果完全类比,或者把这个例子当成是概念本身,那很多细节是经不起推敲的。


黄蓉与欧阳克约定文打之后,欧阳克为什么一定要遵守?黄蓉在打出一拳之后,欧阳克为什么不能动?他完全可以毁约啊。 


这就牵扯到协程和多线程的另一个重要概念了:执行权。






你可以把它想像成是一个令牌,只有拿到令牌的人才能指挥行动。


开始令牌在黄蓉手上,所以她可以行动,欧阳克手上没有令牌,所以只能站着挨打,黄蓉打完之后把令牌丢给了欧阳克,欧阳克才开始还手。


武打就是两人手上都有一个令牌,所以两个人才能乱打一气,同时挥拳。 












有了上面这些基础概念之后,我们可以开始尝试理解问题本身了。
























4







真实的协程












那么真实的协程到底是什么样子?


在单任务系统,依靠切换控制权来模拟多线程的话,那延时必定不可能是真正“延时”。


一个任务的阻塞延时肯定会干扰到其他任务的计时,为了解决这个问题,我们一般会引入一个第三者来进行时间管理,也就是“时钟调度器”。 




合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


其实A、B两个任务在执行时,总共有四个角色存在:Core(底层)、以及Lua中的A任务B任务,还有run (一段控制执行权的代码)。






Core本身带一个系统时钟,用来记录当前时间,同时维护了一个表格,用来记录在什么时间,把控制权交给谁。


A、B在睡觉前都把自己的唤醒时间告诉Core,把自己的控制权交给run。run在获取到唤醒时间之后交给指定任务执行,指定任务执行完毕之后交还控制权。 












当然,这里其实还有另外一个疑问,为什么要有 run 这个角色的存在? 


按照我们直观的理解,为了防止AB在睡觉时霸占控制权,仅仅只需要一个管理者就可以了。时间到达之后,管理者可以自行调用A 或B,它们在执行完毕之后交还控制权,这样看起来似乎更简单。 


这样做其实有两方面原因: 


一是因为除了时钟消息,Core还有其他消息需要传递给Lua,程序中不光是有 A 和 B 提交的时钟消息,所以需要有一个角色来处理这些其他信息。 


二是因为Core和 A、B 两个任务所属在不同层,Core在C 层,而任务A、B是在Lua层。为了简化两个层之间的交互,Core 把消息传递给Lua之后由Lua自行处理调度。所以是Core处理时钟调度信息,Lua负责调度。























5







完整演示
















最后,我们再看一遍整个过程的完整演示:










延时运行图示说明























合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


从上图可以看到,控制权首先是在 Core,Core 完成初始化工作之后把控制权交给任务A执行。




A任务在执行到延时 wait(100) 时会把当前延时时间加上系统时钟时间的数值,连同自身ID,添加进Core的时间链表。也就是告诉 Core,在130这个时间点,把控制权交给A任务。如下图所示:


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


然后到了任务B——任务B在执行到 wait(80) 时,也会向 Core 中添加消息, 在110这个时间点,把控制权交给B任务:


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


接着会按顺序执行到run,run把控制权交给 Core:


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


然后,Core会以1ms的间隔独自计时,每过1ms,Core都会检查链表第一项时间是否达到。当时钟计时达到时间,控制权会交还给run,并且告诉 run,B的计时时间到了。


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事
之后run会把控制权交给B,B执行完返还控制权。当然 B 在执行过程中依然会向Core中添加消息。


点击放大查看:


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


接着就开始下一轮循环,如此往复…


点击放大查看:


合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事

















通过上面的分析我们也不难发现,Lua层当中的所有代码几乎都是瞬时完成的,所有延时操作都是把控制权移交到了Core。























以上就是关于LuaTask延时的一些事,更多开发应用问题,欢迎加入技术群共同探讨交流。















合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


















合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


合宙Luat交流群04:877164555


合宙Luat交流群03:1092305811


合宙Luat交流群18(LuatOS):1061642968  


合宙Luat交流群17(iRTU):1027923658




每个建议都值得关注


每个技能都值得分享

















合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事



- 更多精彩等你参与 -




















了解更多相关内容



















































合宙Luat | 看《射雕英雄传》,聊聊LuaTask延时那些事


















































推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • Linux下安装免费杀毒软件ClamAV及使用方法
    本文介绍了在Linux系统下安装免费杀毒软件ClamAV的方法,并提供了使用该软件更新病毒库和进行病毒扫描的指令参数。同时还提供了官方安装文档和下载地址。 ... [详细]
  • C#多线程解决界面卡死问题的完美解决方案
    当界面需要在程序运行中不断更新数据时,使用多线程可以解决界面卡死的问题。一个主线程创建界面,使用一个子线程执行程序并更新主界面,可以避免卡死现象。本文分享了一个例子,供大家参考。 ... [详细]
author-avatar
naniwang99_537_742
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有