Lua学习笔记4. coroutine协同程序和文件I/O、错误处理

时间:2023-03-08 16:03:10
Lua学习笔记4. coroutine协同程序和文件I/O、错误处理

Lua学习笔记4. coroutine协同程序和文件I/O、错误处理

coroutine

Lua 的协同程序coroutine和线程比较类似,有独立的堆栈、局部变量、独立的指针指令,同时又能共享全局变量

但coroutine又和多线程程序不同,首先一个多线程程序可以同时运行多个单线程,但协同程序只能串行,也就是说同一时刻只能有一个协同程序在运行,并且这个协同程序将一直占用处理器直到被显式的挂起。

基本的函数

coroutine.create(f)         创建一个协同程序,返回coroutine, f是该协同程序的注册函数
coroutine.yield(a) 将当前运行的coroutine挂起,a是挂起后返回的值或表达式结果
coroutine.resume(co,a,...) 唤醒coroutine co,后面的a,...是对应注册函数待输入的参数,返回一个布尔值表示是否唤醒成功
coroutine.status(co) 返回协同程序co的状态,协同程序的状态包括dead,suspend和running
coroutine.running() 返回当前正在运行的coroutine的线程号和一个boolean值表示是不是主线程

看个例子

function foo(a)
print("foo 的函数输出 ", a)
return coroutine.yield(2*a)
end co=coroutine.create(function(a,b)
print("第一次协同程序执行输出",a,b)
local r=foo(a+1)
a=a+1
print("第二次协同程序输出",r)
local r,s=coroutine.yield(a+b,a-b) print("第三次协同程序执行输出",r,s)
return b
end
) print('main',coroutine.resume(co,1,10))
print('--------分割线--------')
print('main',coroutine.resume(co))
print('--------分割线--------')
print("main",coroutine.resume(co,"x","y"))
print("--------分割线--------")
print("main",coroutine.resume(co,"x","y"))
print("--------分割线--------")

其输出

第一次协同程序执行输出 1   10
foo 的函数输出 2
main true 4
--------分割线--------
第二次协同程序输出 nil
main true 12 -8
--------分割线--------
第三次协同程序执行输出 x y
main true 10
--------分割线--------
main false cannot resume dead coroutine
--------分割线--------

分析一下:首先执行到print('main',coroutine.resume(co,1,10))这一行先执行里面作为参数的函数,即唤醒协同程序co,进入了对应的注册函数,输出一句,然后进入函数foo,foo的参数为1,10

执行foo里面的第一句输出,然后返回 挂起线程操作的状态,以及对应的返回值,这里就是2*a

OK,这时候又回到了主线程,输出 main 状态 返回参数

输出分割线

唤醒co,这个时候从上次挂起的阶段继续执行,此时resume的参数是coroutine.yield()的参数作为返回值

。。。

最后如果co已经dead,则输出false,即唤醒失败,和cannot resume dead coroutine的信息,如果这个时候想再执行注册函数的内容需要重新创建coroutine

生产者-消费者问题

local newProductor

function productor()
local i=0
while i<10 do
i=i+1
send(i)
end
end function consumer()
repeat
local i=receive()
print(i)
until i>9
end function receive()
local status,value=coroutine.resume(newProductor)
return value
end function send(x)
coroutine.yield(x)
end newProductor=coroutine.create(productor)
consumer()

I/O

lua的文件输入输出包含两种模式 简单模式和完全模式

简单模式

file = io.open("moduleTest.lua",'r')
io.input(file)
print(io.read())
io.close(file)
file=io.open("moduleTest.lua","a+")
io.output(file)
io.write("This is a sentense written in simple model")
io.close(file)

复杂模式

file=io.open("moduleTest.lua","a+")
print(file:read())
file:close()
file=io.open("moduleTest.lua","a")
file:write("This sentence is written in complete model")
file:close

不同点在于简单模式需要指定输入输出设备,而完全模式直接通过文件句柄操作。

文件open的模式有

r           只读模式,文件必须存在
w 清零重写模式,如果没有则新建
a 附加模式,没有则新建
r+ 读写模式,文件必须存在
w+ 和w类似,但是可读写
a+ 和a类似,但是可读写
b 二进制模式,如果文件是二进制文件,可在上面的模式符后添加b

read()的参数有

空                  读取一行
"*n" 读取一个数字
"*a" 从当前位置读取整个文件
"*l"(默认) 都去下一行,文件结尾处返回nil
number 读取指定个数个字符,EOF时返回nil

io其他方法

io.tmpfile()    返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
io.type(file) 检测file是不是可用的文件句柄
io.flush() 向文件中写入缓存区中所有数据
io.lines(optional filename) 返回一个由line组成的迭代器,文件结尾时返回nil,但不关闭文件
e.g.
for line in io.lines("moduleTest.lua") do
print(line)
end

file的read,write,lines,close等方法和io类似,只不过使用的是":"符号

file的其他方法

file:seek(optional whence,optional offset)  设定当前位置,第二个参数是偏置
第一个参数可选
"set" 文件头
"cur" 当前位置(默认)
"end" 文件尾
不带参数,file:seek()返回当前位置 file:flush() 将缓冲区所有数据写入文件
file:lines() 类似于io.lines(filename)返回一个迭代器,只是此处没有输入参数

错误处理

  1. 断言assert(arg1,arg2),首先检查第一个参数,没问题ok,有问题将第二个参数作为错误信息抛出

  2. error(message[,level]),终止执行的函数,抛出message作为错误信息

    level表示附加的错误位置信息

    level=1,error的位置

    level=2,调用error的函数

    level=0,不添加错误位置信息

  1. 类似于trycatch语句pcall,xpcall

    调用格式:

    pcall(f[,arg]) --f是protect call的函数,arg是f的参数

    xpcall(f,errHandleFun[,arg]) --errHandleFun是错误处理函数

相同点:

当程序正常执行时,都返回true和被执行函数f的返回值

不同点:

i. pcall: 返回错误信息时,已经释放了保存错误发生情况的栈信息

xpcall: 在释放栈信息之前调用错误处理程序处理这些信息

ii. pcall:返回nil,错误信息

xpcall:返回nil,无错误信息

local f=function(...)
--local a=1
print (a+1)
return a+1
end tryCatch = function(f)
local ret,errMessage=pcall(f)
print("ret:" ..(ret and "true" or "false") .."\nerrMessage:"..(errMessage or "null"))
end xTryCatchGetErrorInfo=function()
print(debug.traceback())
end xTryCatch=function(f)
local ret,errMessage=xpcall(f,xTryCatchGetErrorInfo)
print("ret:"..(ret and "true" or "false").."\nerrMessage:"..(errMessage or "null"))
end print("\n---------A---------\n")
tryCatch(f)
print("\n---------B---------\n")
xTryCatch(f)
print("\n---------C---------\n")

输出

---------A---------

ret:false
errMessage:testIO.lua:18: attempt to perform arithmetic on global 'a' (a nil value) ---------B--------- stack traceback:
testIO.lua:28: in function <testIO.lua:27>
testIO.lua:18: in function <testIO.lua:16>
[C]: in function 'xpcall'
testIO.lua:32: in function 'xTryCatch'
testIO.lua:39: in main chunk
[C]: ?
ret:false
errMessage:null ---------C--------- >Exit code: 0