一、协程
协程是单核的,是一个线程下执行的,所以每一时刻只会有一个协程在运行。线程一般由cpu调度,协程由用户调用
1. 协程创建
协程创建有两种方式
1.1 coroutine.create
coroutine.create
:创建协程 coroutine.resume
:启动协程
-- 创建协程
cor1 = coroutine.create(
function(a,b)
print(a+b)
end
)
-- 启动协程
coroutine.resume(cor1,1,2)
运行结果:
1.2 coroutine.wrap
coroutine.wrap
:创建协程 协程变量(入参)
:启动协程
-- 创建协程2
cor2 = coroutine.wrap(
function(a)
print(a)
end
)
cor2(5)
运行结果:
2. 协程的暂停和继续
协程还可以通过代码暂停执行和继续执行
2.1 暂停协程
coroutine.yield
:协程暂停
在定义协程的function
中,执行暂停方法:
-- 暂停、继续协程
cor3 = coroutine.create(
function()
print('准备暂停')
coroutine.yield()
print('继续执行')
end
)
coroutine.resume(cor3)
运行结果:
可以看到print('继续执行')
并没有执行
2.2 继续协程
coroutine.resume
:协程继续
再次调用coroutine.resume
,就可以继续执行协程了:
-- 暂停、继续协程
cor3 = coroutine.create(
function()
print('准备暂停')
coroutine.yield()
print('继续执行')
end
)
coroutine.resume(cor3)
-- 继续执行协程
coroutine.resume(cor3)
运行结果:
3. 返回值和入参
协程执行也有返回值,并且每次执行结束或暂停都有返回值,每次继续都有不同的入参
3.1 执行结束返回值
一个协程正常执行结束,如果不指定return
,那么默认会返回一个true
:
-- 协程执行结束返回值
cor4 = coroutine.create(
function()
print("结束啦")
end
)
-- 接收返回值
ret4 = coroutine.resume(cor4)
print(ret4)
运行结果:
如果协程运行结束后再次运行,那么将返回false
:
-- 协程执行结束返回值
cor4 = coroutine.create(
function()
print("结束啦")
end
)
-- 接收返回值
ret4 = coroutine.resume(cor4)
print(ret4)
print(coroutine.resume(cor4))
运行结果:
如果指定return
,那么将多返回值返回,下面是使用return
的情况:
-- 协程执行结束返回值
cor4 = coroutine.create(
function()
print("结束啦")
return '哈哈'
end
)
-- 接收返回值
ok,ret4 = coroutine.resume(cor4)
print(ok,ret4)
运行结果:
3.2 暂停返回值
上面使用协程暂停和继续时,我们知道了,每次在定义协程的function
中调用yield
,都必须再次调用resume
才能继续执行协程,而接收协程返回值的方法就是resume
,所以猜想每次yield
,都会有返回值,下面就来测试下
打印两次执行协程的返回值:
-- 协程暂停返回值
cor5 = coroutine.create(
function(a)
print('接收参数:',a)
-- 暂停协程
coroutine.yield()
end
)
print(coroutine.resume(cor5,10))
print(coroutine.resume(cor5,10))
运行结果:
yield
方法还可以传入参数,作为每次暂停的返回值:
-- 协程暂停返回值
cor5 = coroutine.create(
function(a)
print('接收参数:',a)
-- 暂停协程
coroutine.yield('哈哈')
end
)
print(coroutine.resume(cor5,10))
print(coroutine.resume(cor5,10))
运行结果:
3.3 继续执行入参
我们的参数都是通过resume
方法传入的,既然yield
可以有暂停协程的返回值,那么每次resume
也可以传入新的入参:
-- 协程暂停返回值
-- 协程暂停、继续返回值
cor5 = coroutine.create(
function(a)
print('第一次接收参数:',a)
-- 暂停协程,并接收新的参数
i,k = coroutine.yield('哈哈')
print('第二次接收参数:',i,k)
end
)
print(coroutine.resume(cor5,10))
print(coroutine.resume(cor5,20,30))
运行结果:
4. 协程的状态
一个协程从定义,到运行,到暂停,到执行结束,它的状态如何变化呢?下面就来探究协程的状态
coroutine.status
可以查看传入协程的状态:
-- 协程状态
cor6 = coroutine.create(
function()
print('运行时状态:',coroutine.status(cor6))
coroutine.yield()
print('恢复运行时状态:',coroutine.status(cor6))
end
)
print('运行前状态:',coroutine.status(cor6))
coroutine.resume(cor6)
print('暂停后状态:',coroutine.status(cor6))
coroutine.resume(cor6)
print('运行完成后状态:',coroutine.status(cor6))
运行结果:
所以上面我们称为暂停协程是不确切的,应该称为挂起,可以看到协程有三种状态:挂起、运行、死亡
5. 协程实现生产者消费者模式
协程可以被挂起和继续,那么实现生产者和消费者就简单多了,消费者执行生产者协程生产,生产者协程生产完成后,将自身挂起,将产品作为返回值返回,消费者进行消费即可
produce = coroutine.create(
function()
local i = 0
while(i < 10) do
i = i + 1
print('生产者生产:',i)
-- 挂起协程
coroutine.yield(i)
end
end
)
function consumer()
while(true) do
-- 执行协程
ok,ret = coroutine.resume(produce)
if ok and ret ~= nil then
print('消费者消费:',ret)
else
break
end
end
end
-- 开始程序
consumer()
运行结果:
二、异常处理
异常分为两种,编译异常和运行时异常
1. 编译异常
当我们语法出现错误时,执行lua脚本时就会报错,这种异常我们无法捕获,只有将代码修改正确
a == 1
if a then
print(a)
end
运行结果:
下面我们只讨论运行时异常
2. 抛出异常
抛出异常有两种方式
2.1 assert断言
asser
判断第一个参数是否为false
,如果是false
,则抛出异常,信息为第二个参数
-- assert
function requireNotNil(a)
assert(a,'必须不为空')
end
requireNotNil()
运行结果:
2.2 error
error
直接抛出一个异常
-- error
function requireNumber(a)
if type(a) ~= type(1) then
error("必须是number类型")
end
end
requireNumber('zhangsan')
运行结果:
3. 处理异常
如果不处理异常,那么程序将会退出,处理异常有两种方式
3.1 pcall
pcall
可以测试函数的执行,第一个参数为函数名,后面参数为入参,如果没有异常,那么返回true
和函数返回值,否则返回false
和异常:
-- error
function requireNumber(a)
if type(a) ~= type(1) then
error("必须是number类型")
end
return a
end
-- requireNumber('zhangsan')
-- pcall
print(pcall(requireNumber,1))
print(pcall(requireNumber,'1'))
运行结果:
3.2 xpcall
xpcall
可以处理异常,只允许两个参数,第一个为调用的函数名,第二个为处理异常的function
:
-- xpcall
function handleException(err)
print('出现异常:',err)
end
function throwException()
error('有个异常')
end
xpcall(throwException,handleException)
运行结果:
三、面向对象
lua中面向对象是通过table
来实现的,table
的元素可以是不同数据类型,也可以是一个函数
1. 函数类型元素
给table设置一个函数,有三种方式
1.1 赋值新值方式
第一种就是给table
赋值新值方式,即初始化和后续新增赋值:
-- 初始化时定义函数
user = {
name = '张三',
age = 18,
doSomething = function(name)
print(name.."做功课")
end
}
user.doSomething(user.name)
-- 后续新增函数
user.sleep = function(name)
print(name.."去睡觉")
end
user.sleep(user.name)
运行结果:
1.2 table名.函数名方式
function table名.函数名
方式,也可以为table
新增一个函数
-- table.函数方式
function user.wakeup(name)
print(name.."醒了")
end
user.wakeup(user.name)
运行结果:
2. 面向对象
如何让上面的user
成为一个类型,被其他变量使用呢?
答案是结合:
和使用元表,在function table名.函数名
方式时,将.
替换成:
,就可以在函数内部使用self
来获取自身,此时使用元表的__index
,将self
和一张空表结合,返回出去,就能得到一张新的user
的table
了
-- 面向对象
function user:new()
u = {}
setmetatable(u,{__index = self})
return u
end
lisi = user:new()
lisi.name = '李四'
lisi.age = 25
print(lisi.name..lisi.age)
print(lisi.doSomething(lisi.name))
print(lisi.sleep(lisi.name))
运行结果:
网友评论