TIME(延时和定时器)

# 简介 在LuatOS-Air脚本程序中,往往需要用到延时和等待等逻辑功能。例如,你想得到每隔30秒查询当前内存,这里就需要用到延时和定时器。在每次打印后加上一个30秒的定时器或者30秒的延迟,既可以实现这个功能。本篇文章将介绍2个延时函数和4个定时器函数以及一个判定定时器激活状态的函数,讲解这些函数如何使用以及在什么环境下使用。 # API说明 > 详细的API介绍见[sys API章节](https://doc.openluat.com/wiki/21?wiki_page_id=2295)和[rtos API章节](https://doc.openluat.com/wiki/21?wiki_page_id=2247) # 实现流程 ## 创建 sys.wait(): * sys.wait(ms):既属于延时也属于定时器,但只能适用于任务函数中(task)。它功能就是被调用时挂起当前协程,等延时时间结束结束后再继续执行剩下的协程功能。原理是先去启动一个定时器,将当前的协程挂起。如需在定时器计时结束前退出,可使用coroutine.resume接口重新激活协程。 创建也很简单,例子:sys.wait(1000) 会停止当前的协程任务,等1000ms后延时函数正常消亡,继续回来执行剩下脚本。 rtos.sleep(): * rtos.sleep(ms):属于底层延时函数,可用于任何环境。函数执行原理:执行该函数会将操作系统中的Lua虚拟机任务挂起,在计时结束前无 法退出。 创建示例:rtos.sleep(1000) 执行后,Lua虚拟机任务就会被挂起,1000ms后函数正常消亡,继续执行Lua虚拟机任务。 接下来是介绍定时器,共有四种定时器,他们各有各的特点。所以我们在使用时一定要记清楚这些定时器的特点,挑选适合当前程序需求的定时器使用。在LuatOS-Air脚本程序中,同时运行的定时器是有数量限制的。8910平台的LuatOS-Air最多支持50个定时器,如何理解50个定时器?在下面的知识扩展里详细讲解了。sys.timerStart()和function sys.timerLoopStart()的返回值是定时器ID,这个可以用来判断是否为同一个 定时器。或者也可以通过定时器的回调函数和回调参数是否相同来判断。 sys.timerStart(): * sys.timerStart(fnc,ms,...):属于定时器,单词定时器,适用场景不限定。第一个参数是回调函数,第二个是超时时间(ms),第三个是回调函数的参数,返回值是定时器ID。在创建前会查找当前开启的定时器,有相同定时器ID或者回调函数和回调参数相同时,就会关闭签一个定时器,再创建新的定时器,重新计时。 function sys.timerLoopStart(): * function sys.timerLoopStart(fnc,ms, ...):属定时器,循环定时器,适用场景不限定。用法特点同单此定时器几乎相同。唯一区别就是单此和多次循环。 sys.wait()(仅适用于task): * function sys.wait(ms):延时介绍过,属于延时也属于定时器。单次定时器,适用场景仅限定task内。 sys.waitUntil()(仅适用于task): * sys.waitUntil(id,ms):属于定时器,单次定时器,适用场景不限定。第一个参数是消息ID,第二个是超时间。sys.waitUntil()的特性与sys.timerStart()的完全一样,区别是sys.waitUntil()仅限于task内部使用。特性与详细介绍看下图。 ![image.png](https://cdn.openluat-luatcommunity.openluat.com/images/20210318111003653_image.png) ## 消亡 ### 自动消亡 1. sys.timerStart:收到定时器事件,执行回调函数后,lib中自动消亡 2. sys.timerLoopStart:收到定时器事件,每次执行回调函数后,lib中重新创建 3. sys.wait:挂起的task收到定时器事件后,lib中自动消亡 4. sys.waitUntil:挂起的task收到消息事件或者定时器事件后,lib中自动消亡 ### 手动消亡 手动消亡有两种方式:sys.timerstop()和sys.timerstopAll() 1.sys.timerstop(val,...) 参数是定时器ID或者定时器的回调函数和回调参数。使用这个函数可以关闭指定的一个定时器。 返回值为nil ![image.png](https://cdn.openluat-luatcommunity.openluat.com/images/20210318154059659_image.png) 2.sys.timerstopAll(fnc) 功能:关闭sys.timerStart和sys.timerLoopStart创建的某一个回调函数的所有定时器 参数:需要关闭的定时器回调函数。 返回值:nil ![image.png](https://cdn.openluat-luatcommunity.openluat.com/images/20210318154846542_image.png) 已经消亡的定时器,再去执行stop不会产生异常 ## 判断定时器状态 **sys.timerIsActive()** 功能:判断“通过timerStart或者timerStart创建的定时器”是否处于激活状态 参数:通过timerStart或者timerStart创建的定时器返回的定时器id或者回调函数与回调参数。 参数: 参数为回调函数与回调参数时,如果处于激活状态,则返回bool类型的true,否则返回nil。 返回值: 参数为定时器ID时,如果处于激活状态,则返回function类型的定时器回调函数,否则返回nil 例子: ```lua local function timerCbFnc2(tag) log.info("timerCbFnc2",tag) end sys.timerStart(timerCbFnc2,5000,"test") sys.taskInit(function() sys.wait(3000) log.info("after 3 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test")) sys.wait(3000) log.info("after 6 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test")) end) ``` 结果: ![image.png](https://cdn.openluat-luatcommunity.openluat.com/images/20210318155924701_image.png) ----- # 知识拓展 1.8910平台的LuatOS-Air最多支持50个定时器,如何理解50个定时器? 看下列两端代码,猜测,最后分别还有几个定时器在运行。 ```lua sys.taskInit(function() for i=1,50 do sys.timerStart(function() end,1000) end log.info("不考虑其它代码中定时器的使用") log.info("运行到这里,同时存在多少个定时器?") end) ``` ```lua sys.taskInit(function() for i=1,50 do sys.wait(1000) end log.info("不考虑其它代码中定时器的使用") log.info("运行到这里,同时存在多少个定时器?") end) ``` 第一段代码是创建50个回调函数相同,延时时间相同的单此定时器。通过前面的介绍可知, 如果回调函数和回调参数都相同的两个sys.timerStart()定时器,可视为同一定时器。当第一个sys.timerStart(function() end,1000)创建好后,再创建第二个sys.timerStart(function() end,1000)时,就会查找当前存在的定时器里,是否有相同的定时器,如果就就会将第一个关闭,创建第二个相同的定时器,并重新开始计时。所以第一个代码段最后吃剩一个定时器在运行。 第二段代码是创建50个sys.wait(1000)。当创建第一个sys.wait(1000)后,这个协程就会被挂起。1000ms后,继续执行剩下的程序,创建下一个sys.wait(1000)。所以,最后也只剩一个定时器。 下面这段代码,通过改变回调参数来同时创建50个sys.timerLoopStart(log.info,1000,"nCounter1",i)。但看打印结果会发现,没到50个。因为打印出来的数量只是sys.timerLoopStart(log.info,1000,"nCounter1",i)的数量,还有sys.wait(5000)等一些定时器没算在内。 ```lua local function wait1() sys.wait(5000) for i=1,50 do sys.timerLoopStart(log.info,1000,"nCounter1",i) end end sys.taskInit(wait1) ``` # 示例 > 详细的API demo见[The Timer demo章节](https://doc.openluat.com/wiki/21?wiki_page_id=2311) 1.waitUntil()、wait() * waitUntil()和wait()sys.wait和sys.waitUntil只能用在task中,或者被task的主函数直接或者间接调用。 ```lua local function IndirectCall() sys.waitUntil("MSG_ID",5000) end sys.taskInit(function() sys.wait(5000) IndirectCall() log.info("sys.wait和sys.waitUntil只能用在task中") log.info("只能被task的主函数直接或者间接调用") end) ``` 2.timerStart() * timerStart()是单此定时器 ```lua local function publicTimerCbFnc(tag) log.info("publicTimerCbFnc",tag) end sys.timerStart(publicTimerCbFnc,8000,"first") local timerId2 = sys.timerStart(publicTimerCbFnc,8000,"second") sys.timerStart(publicTimerCbFnc,8000,"third") ``` 3.timerLoopStart() * timerLoopStart()是循环定时器 ```lua local nCounter = 0 sys.taskInit(function () while true do nCounter = nCounter+1 sys.wait(1000) end end) sys.timerLoopStart(log.info,1000,"nCounter1",nCounter) --sys.timerLoopStart(log.info,1000,"nCounter1",0) sys.timerLoopStart(function() log.info("nCounter2",nCounter) end,1000)0,"first") ``` # 常见问题 ## 1,定时器精度问题 LuatOS-Air开发最小仅支持5毫秒的定时器,超时时间最小1ms(实际支持的最小时间是5ms,小于5ms的时间都被转化为5ms) 超时间最大0x7FFFFFFF(24.85天)。 另外毫秒级的定时器的误差较大,原因可参考[LuatOS-Air应用脚本运行框架](https://doc.openluat.com/wiki/21?wiki_page_id=1926) ## 2,一个项目中sys.timerLoopStart定时器,能开几个?有没有限制? LuatOS-Air最多支持50个定时器,详细说明看上面 [知识拓展](https://hmi.wiki.luatos.com/doc/65042949/e6zPC3k9/MgfM7VP0#nav_9 "知识拓展") ## 3,sys.timerLoopStart定时器的值,可以用变量动态改动吗? 不可以,第一次调用的时候会把值传进去,之后会用第一次传的值,不会随着变量值的变化而改变,测试效果如下: ```lua local testm = 1000 local function test() print("test testm",testm) testm = testm + 1000 end sys.timerLoopStart(test,testm) ``` ![image.png](https://cos.easydoc.net/22753220/files/l0c4ty9r.png)