- 论坛徽章:
- 5
|
说实话没看懂,这样,我说一下coroutine和回调的联系吧。
假设我有一个回调接口在Lua上,这个接口的实现是很容易的,假设回调是这样的:
local server = require 'server' .new()
servern_connect(function(...)
...
end)
server:loop()
然后在连接的时候,调用on_connect注册的函数,当然还可以有很多的函数。在loop的时候等待客户端的连接,在accept的时候执行这个回调,这很明白了吧?
所谓的“coroutine”的优势,其实只有在多个回调的情况下才会出现。好,现在我们假设除了server以外,我们还有一个模块叫client,这个client模块负责产生某些连接:
local client = require 'client' .new()
client:connect(function(...)
....
en)
client:connect(function(...)
....
end)
client:loop()
我们创建了两个不同的连接,然后loop中对连接的结果进行回调,这个很简单吧?具体的实现大概是这样的:每次connect都会产生新的fd,然后会通过select/epoll完成loop的内容。注意!无论是select/epoll,都能在返回的时候实时反馈所有fd的状态。因此,在这两个函数返回的时候,你可以选择:
1. 调用有事件的函数的回调(比如cb("read", data...)或者cb("write", status...))
2. 当fd出错被关闭,将某些资源(主要是函数)标记为可以回收,当然回收前通知一下也可以
关键是!epoll不是通过轮询去获知所有的fd的状态的,而是会直接返回状态有改变的fd。对Lua来说,这也是一样的。
现在回到coroutine话题。这和回调是一一对应的。
1. 回调的注册,类似于心的coroutine的创建
2. 对回调的调用,类似于对coroutine的lua_resume
3. 对回调资源的清理,类似于对coroutine本身资源的清理
我们举个实际的例子。
假设我同时下载两个网站A和B的资源。现在用coroutine做这件事情。代码如下:
local res, err = client:download("A"
if err then ... end
print(res)
那么,操作是这样的:
1. 将上面的chunk做成一个function:lua_load(....)
2. 创建一个新的coroutine:lua_newthread(L);
3. 将chunk放到coroutine上面去:lua_xmove(或者直接在coroutine上lua_load)
4. 将coroutine和fd关联:registry.callbacks[fd] = co
5. loop的时候epoll/select
6. 当得知某个fd的状态改变时,查询到对应的co:co = registry.callbacks[fd]
7. 调用这个coroutine:lua_resume(co, ...)
8. 在清理fd的时候,清理那个注册表:registry.callbacks[fd] = nil
就是这样的。完全不需要轮询。 |
|