1. 程式人生 > >Lua Web快速開發指南(9)

Lua Web快速開發指南(9)

API 介紹

cf框架提供內建的非同步庫cf, 需要使用的時候我們必須先匯入API: local cf = require "cf".

定時器與迴圈定時器

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

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

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

  • 當時間引數等於0的時候, 當前協程會暫停並且讓出執行權. 當其它協程執行完畢(讓出)後立刻返回.

  • 當時間引數小於0或者非number型別的時候, 此方法將立刻返回.

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

cf.timeoutcf.at都會返回一個timer物件, 開發者可以在任何時候使用timer物件的stop方法停止定時器.

cf.timeoutcf.at的引數如下:

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

  • 第二個引數是一個回撥函式, 當時間事件觸發後將會為使用者執行使用者定義的回撥函式.

記住: 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庫來完成訊息