模拟读者读书

# 首先新建 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
}