概述
在異步操作中,常常要使用回呼,但是,回呼的嵌套常常會導致邏輯混亂,一步錯步步錯,難以維護,在Lua中,可以使用協程進行優化,
問題分析
模擬一個回合制游戲攻擊程序
local function PlayAnim(anim, cb)
print("開始播放 " .. anim)
os.execute("sleep " .. 1)
print("播放完成 " .. anim)
cb()
end
local function Main()
print("行動開始")
PlayAnim("移動到目標影片",function()
print("開始攻擊")
PlayAnim("攻擊影片",function()
print("回傳到原位置")
PlayAnim("回傳影片",function()
print("行動結束")
end)
end)
end)
end
Main()
輸出:

- 可以看到異步回呼的嵌套導致代碼結構混亂
Lua協程
簡介
- Lua 協同程式(coroutine)與執行緒比較類似:擁有獨立的堆疊,獨立的區域變數,獨立的指令指標,同時又與其它協同程式共享全域變數和其它大部分東西,
- 執行緒與協同程式的主要區別在于,一個具有多個執行緒的程式可以同時運行幾個執行緒,而協同程式卻需要彼此協作的運行,在任一指定時刻只有一個協同程式在運行,并且這個正在運行的協同程式只有在明確的被要求掛起的時候才會被掛起,
- 協同程式有點類似同步的多執行緒,在等待同一個執行緒鎖的幾個執行緒有點類似協同,
- coroutine在底層實作就是一個執行緒,
API
| API | 說明 |
|---|---|
| coroutine.create() | 創建 coroutine,回傳 coroutine, 引數是一個函式,當和 resume 配合使用的時候就喚醒函式呼叫 |
| coroutine.resume() | 重啟 coroutine,和 create 配合使用 |
| coroutine.yield() | 掛起 coroutine,將 coroutine 設定為掛起狀態,這個和 resume 配合使用能有很多有用的效果 |
| coroutine.status() | 查看 coroutine 的狀態,注:coroutine 的狀態有三種:dead,suspended,running |
| coroutine.wrap() | 創建 coroutine,回傳一個函式,一旦你呼叫這個函式,就進入 coroutine,和 create 功能重復 |
| coroutine.running() | 回傳正在跑的 coroutine,一個 coroutine 就是一個執行緒,當使用running的時候,就是回傳一個 corouting 的執行緒號 |
詳細介紹
請參考跳轉鏈接:菜鳥教程-協程
解決方案
function AsyncFunc(func)
return function(...)
local current_co = coroutine.running()
local ret, res = false, nil
local function cb(...)
if not coroutine.resume(current_co, ...) then
ret = true
res = ...
end
end
local params = table.pack(...)
table.insert(params, cb)
func(table.unpack(params))
if not ret then
res = coroutine.yield()
end
return res
end
end
function BeginTask(func, ...)
local t = coroutine.create(func)
coroutine.resume(t, ...)
end
local function PlayAnim(anim, cb)
print("開始播放 " .. anim)
os.execute("sleep " .. 1)
print("播放完成 " .. anim)
cb()
end
local AsyncPlayAnim = AsyncFunc(PlayAnim)
local function Main()
print("行動開始")
AsyncPlayAnim("移動到目標影片")
print("開始攻擊")
AsyncPlayAnim("攻擊影片")
print("回傳到原位置")
AsyncPlayAnim("回傳影片")
print("行動結束")
end
BeginTask(Main)
輸出:

- 用AsyncFunc和BeginTask分別對異步函式和協程創建和運行做了封裝
- 注意:不能在主協程中運行AsyncFunc,用BeginTask開啟一個新的協程運行Main
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/508799.html
標籤:其他
上一篇:leetcode 637. Average of Levels in Binary Tree 二叉樹的層平均值(簡單)
