Lua Web快速開發指南(9) - 使用cf內置的異步庫

API 介紹

cf框架提供內置的異步庫cf, 須要使用的時候咱們必須先導入API: local cf = require "cf".git

定時器與循環定時器

cf庫內置了一些定時器方法, 這些方法爲開發者提供了對時間事件的控制能力. cf.timeoutcf.atcf.sleep.github

cf.sleep方法是一個阻塞的定時器, 只有一個參數用來設置當前協程的休眠時間而且沒有返回值. 此方法的行爲(語義)取決於用戶傳入的參數:bash

  • 當時間參數大於0的時候, 當前協程會暫停指定的時間且讓出執行權. 當指定的時間超時後函數將會返回繼續執行下面的代碼.框架

  • 當時間參數等於0的時候, 當前協程會暫停而且讓出執行權. 當其它協程執行完畢(讓出)後馬上返回.dom

  • 當時間參數小於0或者非number類型的時候, 此方法將馬上返回.異步

cf.timeoutcf.at不會阻塞當前協程執行流程. 目前雖然暴露給開發者使用, 但真正的使用場景都僅限於在須要長鏈接業務內.函數

cf.timeoutcf.at都會返回一個timer對象, 開發者能夠在任什麼時候候使用timer對象的stop方法中止定時器.學習

cf.timeoutcf.at的參數以下:ui

  • 第一個參數是一個指定的時間, 其在現實中的時間比例爲1:1.lua

  • 第二個參數是一個回調函數, 當時間事件觸發後將會爲用戶執行用戶定義的回調函數.

記住: cf.timeout是一次性定時器, 回調函數被觸發以後將會自動中止運行. 而cf.at若是不使用stop方法中止則會一直重複執行.

協程的使用、暫停、喚醒

cf庫提供了協程的操做方法. 此協程與Lua的原生協程有些許不一樣, cf基於原生協程的基礎上由框架管理生命週期.

須要異步執行一個函數可使用cf.fork建立一個由cf調度的協程, 此方法會返回一個協程對象. 這個協程對象能夠在它讓出的時候用來主動喚醒.

cf.fork方法的第一個參數func爲function類型, 從第二個參數開始的參數將會做爲func的參數(通常狀況下咱們會利用upvalue而不會顯示傳遞參數).

須要暫停一個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庫來完成消息隊列的任務監聽.

相關文章
相關標籤/搜索