模拟读者读书
# 首先新建 reader 类
package main | |
import "fmt" | |
type Reader struct { | |
Uid uint32 | |
UserName string | |
ReaderCount uint8 | |
} | |
func (reader *Reader) read(book string) { | |
reader.ReaderCount++ | |
fmt.Printf("Reader:%v,Name:%v,read book %v\n", reader.Uid, reader.UserName, book) | |
} |
# 将 reader 类注册到 lua 中
package main | |
import lua "github.com/yuin/gopher-lua" | |
const luaPersonTypeName = "reader" | |
var readerMethods = map[string]lua.LGFunction{ | |
"read": luaReaderRead, | |
"username": readerGetSetUsername, | |
} | |
// 注册定义的类成为 lua 的一个元表 | |
func registerReaderType(L *lua.LState) { | |
mt := L.NewTypeMetatable(luaPersonTypeName) | |
L.SetGlobal("reader", mt) | |
L.SetField(mt, "new", L.NewFunction(luaNewReader)) | |
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), readerMethods)) | |
} | |
//lua 创建对象方法 | |
func luaNewReader(L *lua.LState) int { | |
reader := &Reader{ | |
uint32(L.CheckInt(1)), | |
L.CheckString(2), | |
uint8(L.CheckInt(3)), | |
} | |
ud := L.NewUserData() | |
ud.Value = reader | |
L.SetMetatable(ud, L.GetTypeMetatable(luaPersonTypeName)) | |
L.Push(ud) | |
return 1 | |
} | |
// 在 lua 中获取对象的重要一步 | |
func checkReader(L *lua.LState) *Reader { | |
ud := L.CheckUserData(1) | |
if v, ok := ud.Value.(*Reader); ok { | |
return v | |
} | |
L.ArgError(1, "reader expected") | |
return nil | |
} | |
// 方法注册到 lua 中 | |
func luaReaderRead(L *lua.LState) int { | |
r := checkReader(L) | |
book := L.ToString(2) | |
r.read(book) | |
return 1 | |
} | |
// 属性的 get Set 方法, 注意方法名必须这样写:结构名 GetSet 属性名,大小写也要注意 | |
func readerGetSetUsername(L *lua.LState) int { | |
r := checkReader(L) | |
if L.GetTop() == 2 { | |
r.UserName = L.CheckString(2) | |
return 0 | |
} | |
L.Push(lua.LString(r.UserName)) | |
return 1 | |
} |
# 也许有一些模块需要注入到 lua 中
package main | |
import ( | |
"fmt" | |
lua "github.com/yuin/gopher-lua" | |
) | |
var modFuncs = map[string]lua.LGFunction{ | |
"eat": Eat, | |
"drink": Drink, | |
"record": Record, | |
} | |
func Eat(L *lua.LState) int { | |
msg := L.CheckString(1) | |
fmt.Println("eat:", msg) | |
return 0 | |
} | |
func Drink(L *lua.LState) int { | |
msg := L.CheckString(1) | |
fmt.Println("drink:", msg) | |
return 0 | |
} | |
func Record(L *lua.LState) int { | |
r := checkReader(L) | |
fmt.Printf("%v读完了!一共%v本书!\n", r.UserName, r.ReaderCount) | |
return 1 | |
} | |
func Loader(L *lua.LState) int { | |
mod := L.SetFuncs(L.NewTable(), modFuncs) | |
L.SetField(mod, "mymod", lua.LString("value")) | |
L.Push(mod) | |
return 1 | |
} |
# 预先定义一个 lua 文件
这样所有的协程可以共享这个 lua 文件
local mymod =require("mymod") -- 加载注入的模块 | |
function init() | |
global_id = 1 | |
global_name = "test" | |
end | |
function newReader() | |
r = reader.new(global_id,global_name,0) | |
end | |
-- 连续执行三次 | |
function read(book) | |
r:read(book) | |
mymod.eat("面包") | |
mymod.drink("雪碧") | |
end | |
function finish() | |
mymod.record(r) | |
end |
# 然后可以试试看啦
package main | |
import ( | |
"bufio" | |
"fmt" | |
"github.com/yuin/gopher-lua" | |
"github.com/yuin/gopher-lua/parse" | |
"math/rand" | |
"os" | |
"strconv" | |
"sync" | |
"time" | |
) | |
// TODO: 加载 lua 代码执行 | |
// TODO: 多线程 | |
var wg sync.WaitGroup | |
func main() { | |
books := []string{ | |
"活着", "白鹿原", "春秋战国", "兄弟", "许三观卖血记", "丰乳肥臀", | |
} | |
luaPath := "./main/test.lua" | |
luaProto, err := compileFile(luaPath) | |
if err != nil { | |
fmt.Println(err) | |
return | |
} | |
for i := 0; i < 100; i++ { | |
wg.Add(1) | |
go DoRead(luaProto, uint32(i), "Reader"+strconv.Itoa(i), books) | |
} | |
wg.Wait() | |
} | |
// 机器人主流程 | |
func DoRead(luaProto *lua.FunctionProto, id uint32, name string, books []string) { | |
fmt.Println(id) | |
L := lua.NewState() | |
defer L.Close() | |
registerReaderType(L) | |
L.PreloadModule("mymod", Loader) // 注入自己的模块 | |
lFunc := L.NewFunctionFromProto(luaProto) // 从字节码解析得到 | |
L.Push(lFunc) | |
L.PCall(0, lua.MultRet, nil) | |
// init | |
if err := L.CallByParam(lua.P{ | |
Fn: L.GetGlobal("init"), | |
NRet: 0, | |
Protect: true, | |
}, lua.LNil); err != nil { | |
fmt.Println(err) | |
} | |
// 新建机器人 | |
L.SetGlobal("global_id", lua.LNumber(id)) | |
L.SetGlobal("global_name", lua.LString(name)) | |
if err := L.CallByParam(lua.P{ | |
Fn: L.GetGlobal("newReader"), | |
NRet: 0, | |
Protect: true, | |
}, lua.LNil); err != nil { | |
fmt.Println(err) | |
} | |
// 读书 | |
for i := 0; i < 3; i++ { | |
book := books[rand.Int()%len(books)] | |
if err := L.CallByParam(lua.P{ | |
Fn: L.GetGlobal("read"), | |
NRet: 0, | |
Protect: true, | |
}, lua.LString(book)); err != nil { | |
fmt.Println(err) | |
} | |
time.Sleep(time.Second) | |
} | |
// 结束 | |
if err := L.CallByParam(lua.P{ | |
Fn: L.GetGlobal("finish"), | |
NRet: 0, | |
Protect: true, | |
}, lua.LNil); err != nil { | |
fmt.Println(err) | |
} | |
wg.Done() | |
} | |
// 解析文件变成 lua 字节码 | |
func compileFile(filePath string) (*lua.FunctionProto, error) { | |
file, err := os.Open(filePath) | |
defer file.Close() | |
if err != nil { | |
return nil, err | |
} | |
reader := bufio.NewReader(file) | |
chunk, err := parse.Parse(reader, filePath) | |
if err != nil { | |
return nil, err | |
} | |
proto, err := lua.Compile(chunk, filePath) | |
if err != nil { | |
return nil, err | |
} | |
return proto, nil | |
} |