1. 概念
事件驱动
:事件驱动是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。事件驱动的核心自然是事件,从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。
2. 作用
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。基于事件驱动的程序可以实时响应所关心的事件,可以实现模块之间的解耦、实现异步任务、跟踪状态变化。
3. 应用场景
Windows 本身是基于事件驱动模型的,目前大部分的UI编程也都是事件驱动模型。比如你现在的鼠标点击,按下鼠标就会产生一个onClick()事件,因为平台有注册相应的回调事件,每当点击就会触发该事件,然后交给对应的事件处理器进行处理。
事件驱动的大体逻辑如下:
- 存在一个消息队列,用于存储的各种事件
- 当触发了某个事件,就向队列中添加该事件
- 有循环不断从队列中取出事件,根据派发的不同事件,做相应的处理操作,调用不同的函数处理
- 事件一般都保存有自己的处理函数指针,每个事件都有独立的处理函数
4. 具体实现
个人简单理解:
事件驱动就是首先得有一个队列用于缓存事件,相当于一个信道。队列尾会一直添加触发的事件消息,之后有一个处理事件的循环,一直会从队列头取出事件,并执行事件自带的回调函数。
下面用事件驱动的方式【逆序一个文件】
1. 异步IO库
readline
:读事件writeline
:写事件stop
:停止事件loop
:处理事件的循环
文件名:async-lib.lua
#!/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' ]
结束循环工作-------