1. 概念
事件驱动:事件驱动是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。事件驱动的核心自然是事件,从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。
2. 作用
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。基于事件驱动的程序可以实时响应所关心的事件,可以实现模块之间的解耦、实现异步任务、跟踪状态变化。
3. 应用场景
Windows 本身是基于事件驱动模型的,目前大部分的UI编程也都是事件驱动模型。比如你现在的鼠标点击,按下鼠标就会产生一个onClick()事件,因为平台有注册相应的回调事件,每当点击就会触发该事件,然后交给对应的事件处理器进行处理。
事件驱动的大体逻辑如下:
存在一个消息队列,用于存储的各种事件
当触发了某个事件,就向队列中添加该事件
有循环不断从队列中取出事件,根据派发的不同事件,做相应的处理操作,调用不同的函数处理
事件一般都保存有自己的处理函数指针,每个事件都有独立的处理函数
4. 具体实现
个人简单理解:
事件驱动就是首先得有一个队列用于缓存事件,相当于一个信道。队列尾会一直添加触发的事件消息,之后有一个处理事件的循环,一直会从队列头取出事件,并执行事件自带的回调函数。
下面用事件驱动的方式【逆序一个文件】
1. 异步IO库
readline:读事件
writeline:写事件
stop:停止事件
loop:处理事件的循环
#!/usr/local/bin/lua -- local queue = require "deque" -- 需要配置 path dofile("../dataStructure/deque.lua") -- 路径不用管,就是导入自定义的deque local queue = deque() local lib = {} function lib.readline(stream, callback) local cmd = function() callback(stream:read()) end queue.push_back(cmd) end function lib.writeline(stream, line, callback) local cmd = function() callback(stream:write(line)) end queue.push_back(cmd) end function lib.stop() queue.push_back("stop") end function lib.loop() print("开始循环工作-------") while true do local cmd = queue.pop_front() if cmd == "stop" then print("[ cmd = 'stop' ]") break end cmd() end print("结束循环工作-------") end return lib
2. 事件驱动
可以忽略一些打印信息的代码,方便之后的输出信息查看,理解运行的逻辑。
先调用getline进入文件准备开始读取第一行,然后执行loop开始去取第一条指令。这里getline是将自己作为回调函数注册进readline,指令一直运行相当于递归一直调用getline,并且从流中读入的数据作为getline的参数line存入表t中。
在读到文件尾,getline中会调用putline函数,同理递归的调用putline,逆序的输出了文件内容。
作为一种典型的事件驱动场景,由于主循环位于库async-lib.lua中,因此所有的循环都消失了,这些循环被以事件区分的递归调用所取代。
文件名:event_file.lua
#!/usr/local/bin/lua local lib = require "async-lib" local file_in = io.open("file_in.txt", "a+") local input = io.input(file_in) local file_out = io.open("file_out.txt", "w+") local output = io.output(file_out) local t = {} local i = 0 local label_putline = 0 local label_getline = 0 local function putline() if label_putline == 0 then print("----------------------------------------") print("getline 已完成使命(读完文件所有内容)") print("----------------------------------------") end print("现在调用: putline()") i = i - 1 if i == 0 then lib.stop() else lib.writeline(output, t[i] .. "\n", putline) print(string.format("第[ %d ]写入", label_putline + 1)) label_putline = label_putline + 1 end end local function getline(line) if label_getline == 0 then print("----------------------------------------") print("getline 开始读文件内容") print("----------------------------------------") end print("现在调用:getline(line)") if line then t[#t + 1] = line lib.readline(input, getline) print(string.format("第[ %d ]读入", label_getline + 1)) label_getline = label_getline + 1 else i = #t + 1 putline() end end lib.readline(input, getline) -- 读取第一行开始 lib.loop() -- 运行主循环 io.close(file_in) io.close(file_out)
3. 协程重写
协程可以让我们使用事件循环来简化循环的代码,其核心思想是使用协程运行主要代码,即在每次调用库时将回调函数设置为唤醒协程的函数,然后让出执行权。
下面例子是使用异步库运行同步代码:
文件名:coroutine_file.lua
#!/usr/local/bin/lua local lib = require "async-lib" function run(code) local co = coroutine.wrap(function() code() lib.stop() -- 所有code逻辑运行完,停止 end) co() -- 开始运行,然后进到code()逻辑 -- 阻塞在第一次yield,然后执行lib.loop lib.loop() -- 开始事件循环 end function putline(stream, line) local co = coroutine.running() local callback = (function() coroutine.resume(co) end) lib.writeline(stream, line, callback) coroutine.yield() end function getline(stream, line) local co = coroutine.running() local callback = (function(l) coroutine.resume(co, l) end) lib.readline(stream, callback) local line = coroutine.yield() return line end run(function() local t = {} local file_in = io.open("file_in.txt", "a+") local file_out = io.open("file_out.txt", "w+") local input = io.input(file_in) local output = io.output(file_out) while true do local line = getline(input) if not line then break end t[#t + 1] = line end for i = #t, 1, -1 do putline(output, t[i] .. "\n") end io.close(file_in) io.close(file_out) end)
文件:file_in.txt
start 1 line 2 line 3 line 4 line 5 line end
文件:file_out.txt
end 5 line 4 line 3 line 2 line 1 line start
输出
开始循环工作------- ---------------------------------------- getline 开始读文件内容 ---------------------------------------- 现在调用:getline(line) 第[ 1 ]读入 现在调用:getline(line) 第[ 2 ]读入 现在调用:getline(line) 第[ 3 ]读入 现在调用:getline(line) 第[ 4 ]读入 现在调用:getline(line) 第[ 5 ]读入 现在调用:getline(line) 第[ 6 ]读入 现在调用:getline(line) 第[ 7 ]读入 现在调用:getline(line) ---------------------------------------- getline 已完成使命(读完文件所有内容) ---------------------------------------- 现在调用: putline() 第[ 1 ]写入 现在调用: putline() 第[ 2 ]写入 现在调用: putline() 第[ 3 ]写入 现在调用: putline() 第[ 4 ]写入 现在调用: putline() 第[ 5 ]写入 现在调用: putline() 第[ 6 ]写入 现在调用: putline() 第[ 7 ]写入 现在调用: putline() [ cmd = 'stop' ] 结束循环工作-------