Lua Web快速开发指南(9) - 使用cf内置的异步库

API 介绍

cf框架提供内置的异步库cf, 须要使用的时候咱们必须先导入API: local cf = require "cf".git

定时器与循环定时器

cf库内置了一些定时器方法, 这些方法为开发者提供了对时间事件的控制能力. cf.timeoutcf.atcf.sleep.github

cf.sleep方法是一个阻塞的定时器, 只有一个参数用来设置当前协程的休眠时间而且没有返回值. 此方法的行为(语义)取决于用户传入的参数:bash

  • 当时间参数大于0的时候, 当前协程会暂停指定的时间且让出执行权. 当指定的时间超时后函数将会返回继续执行下面的代码.
  • 当时间参数等于0的时候, 当前协程会暂停而且让出执行权. 当其它协程执行完毕(让出)后马上返回.
  • 当时间参数小于0或者非number类型的时候, 此方法将马上返回.

cf.timeoutcf.at不会阻塞当前协程执行流程. 目前虽然暴露给开发者使用, 但真正的使用场景都仅限于在须要长链接业务内.框架

cf.timeoutcf.at都会返回一个timer对象, 开发者能够在任什么时候候使用timer对象的stop方法中止定时器.dom

cf.timeoutcf.at的参数以下:异步

  • 第一个参数是一个指定的时间, 其在现实中的时间比例为1:1.
  • 第二个参数是一个回调函数, 当时间事件触发后将会为用户执行用户定义的回调函数.

记住: cf.timeout是一次性定时器, 回调函数被触发以后将会自动中止运行. 而cf.at若是不使用stop方法中止则会一直重复执行.函数

协程的使用、暂停、唤醒

cf库提供了协程的操做方法. 此协程与Lua的原生协程有些许不一样, cf基于原生协程的基础上由框架管理生命周期.学习

须要异步执行一个函数可使用cf.fork建立一个由cf调度的协程, 此方法会返回一个协程对象. 这个协程对象能够在它让出的时候用来主动唤醒.ui

cf.fork方法的第一个参数func为function类型, 从第二个参数开始的参数将会做为func的参数(通常状况下咱们会利用upvalue而不会显示传递参数).lua

须要暂停一个cf建立的协程可使用cf.wait方法. 此方法没有参数, 但若是调用此方法的协程不是由cf建立或不是main协程则会出错.

cf.wakeup方法用于唤醒由cf.wait暂停的协程. cf.wait方法的返回值由cf.wakeup的行为决定, 当唤醒的是不存在的协程或唤醒正在执行的协程将会出错.

cf.wakeup方法的第一个参数是一个协程对象, 协程对象以后的全部参数将会返回给cf.wait进行接收.

须要获取当前协程对象的时候在这个协程执行流程之间使用cf.self方法获取, cf.self的做用与内置库coroutine.running方法相同.

它返回一个协程对象与一个boolean值. 当协程对象为主(main)协程时则bolean为true, 不然为false.

更多详细的API介绍

更多使用介绍请参考cf库的文档.

开始实践

1. 随机生成三个定时器而且输出时间.

在本示例中! 咱们首先修改随机数生成器种子, 随机从0~1中间取一个随机数做为定时器的时间. 而后启动一个循环开始生成3个定时器.

-- main.lua
local cf = require "cf"
math.randomseed(os.time()) -- 设置随机数种子
for index = 1, 3 do
  local time = math.random()
  cf.timeout(time, function()
    print("第"..index.."个定时器的时间为:"..time)
  end)
end

因为是随机生成的时间, 因此咱们在函数内部使用print方法将当前定时器的运行信息打印出来(第几个建立的定时器与定时器时间).

如今让咱们多运行几回来查看输出有什么不一样:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
第2个定时器的时间为:0.0029842634685338
第1个定时器的时间为:0.12212080322206
第3个定时器的时间为:0.38623028574511
[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
第1个定时器的时间为:0.10055952938274
第3个定时器的时间为:0.30804532766342
第2个定时器的时间为:0.32007071143016
[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
第3个定时器的时间为:0.083867806941271
第2个定时器的时间为:0.52678858255967
第1个定时器的时间为:0.74910803744569

能够看到, 每次的输出内容由于随机数产生的数值不一样而不一样.

2. 定时器的启动与暂停

一个定时器的启动与中止必然是相对应的. 下面这个示例展现了如何启动与

-- main.lua
local cf = require "cf"
local timer = cf.timeout(1, function ()
  print("定时器触发")
end)

timer:stop()

在上述这段代码中, 咱们启动了一个1秒的一次性定时器而且获取了一个timer的对象.

这个定时器会在超时主动中止后中止运行, 即便屡次对同一个定时器对象调用stop方法也是无害的操做.

若是不出意外的状况下, 开发者应该会看到这样的输出内容:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
定时器触发
[candy@MacBookPro:~/Documents/core_framework] $

若是您注释掉timer:stop()这段代码, 将不会有任何内容输出.

3. 启动一个协程来实现异步任务.

cf的协程是任务执行的关键模块, 咱们利用协程能够达到异步任务的使用效果.

下面这个示例展现了如何使用协程来执行异步任务:

-- main.lua
local cf = require "cf"

print("主协程开始运行..")

cf.fork(function ()
  print("cf的协程开始运行..")
end)

print("主协程开始休眠..")
cf.sleep(1)
print("主协程结束休眠..")

首先咱们在主协程中建立了一个cf协程, 这个线程将在主协程睡眠(让出执行权)期间运行.

当主协程休眠结束后将继续运行. 因此, 它的输出应该是这样子的:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
主协程开始运行..
主协程开始休眠..
cf的协程开始运行..
cf的协程结束运行..
主协程结束休眠..

4. 协程之间的互相做用

咱们来假设一个场景: 主协程建立一个协程执行循环计算任务, 当任务执行完毕后唤醒主协程而且将计算结果传递过来.

思路: 首先咱们利用前面学到的API获取主协程对象, 而后建立一个新的协程来执行计算. 计算完成以后将结果返回.

-- main.lua
local cf = require "cf"

local co = cf.self()
print("主协程开始运行..")

cf.fork(function ()
  print("cf协程开始运行..")
  local result = 0
  for index = 1, 100 do
    result = result + index
  end
  print("cf协程运行完毕, 返回结果而且唤醒主协程..")
  return cf.wakeup(co, result)
end)

print("主协程休眠等待计算完成..")
local result = cf.wait()
print("主协程休眠结束获取到结果为:"..result)
print("主协程执行完毕..")

输出结果以下所示:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
主协程开始运行..
主协程休眠等待计算完成..
cf协程开始运行..
cf协程运行完毕, 返回结果而且唤醒主协程..
主协程休眠结束获取到结果为:5050
主协程执行完毕..

注意: 上述示例仅用于演示如何建立异步任务, 对CPU密集型运算毫无帮助. 真实使用场景通常是在IO密集型运算中使用.

5. 两种不一样的循环定时器

首先咱们根据上述的API建立标准API提供的循环定时器:

local cf  = require "cf"

local timer
local index = 1

timer = cf.at(1, function ()
  if index > 10 then
    print("定时器中止运行..")
    return timer:stop()
  end
  print("输出的数值为:", index)
  index = index + 1
end)

-- timer:stop()

输出以下:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
输出的数值为:    1
输出的数值为:    2
输出的数值为:    3
输出的数值为:    4
输出的数值为:    5
输出的数值为:    6
输出的数值为:    7
输出的数值为:    8
输出的数值为:    9
输出的数值为:    10
定时器中止运行..
[candy@MacBookPro:~/Documents/core_framework] $

上述代码在每次循环定时器超时的时候都会执行回调函数输出当前自增数值, 最后在达到必定次数后自动中止运行. 这种形式的写法也是做者所推荐的写法.

固然, 咱们还有一种利用cf.sleep特殊的定时器写法:

cf.fork(function ()
  for index = 1, 10 do
    cf.sleep(1)
    print("输出的数值为:", index)
  end
  print("定时器中止运行..")
end)

它的输出以下:

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
输出的数值为:    1
输出的数值为:    2
输出的数值为:    3
输出的数值为:    4
输出的数值为:    5
输出的数值为:    6
输出的数值为:    7
输出的数值为:    8
输出的数值为:    9
输出的数值为:    10
定时器中止运行..
[candy@MacBookPro:~/Documents/core_framework] $

两种写法虽然行为上是一致, 可是两种不一样的定时器的内部实现行为倒是不同.

第一种定时器不管在可读性与可控性上来看都作的很是好. 第二种虽然能模拟第一种的行为, 可是在内部须要多建立一个协程来实现唤醒而且没法被外部中止.

并且必须使用第二种定时器的场景通常是不存在的, 可是为了演示仍是须要知会开发者尽可能不要编写毫无好处的代码 :).

继续学习

下一章咱们将学习如何使用MQ库来完成消息队列的任务监听.

相关文章
相关标签/搜索