js css html

lua--协程、异常处理、面向对象

2022-06-22  本文已影响0人  aruba

一、协程

协程是单核的,是一个线程下执行的,所以每一时刻只会有一个协程在运行。线程一般由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和一张空表结合,返回出去,就能得到一张新的usertable

-- 面向对象
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))

运行结果:

上一篇下一篇

猜你喜欢

热点阅读