1. 协程只能在Lua代码中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
c = require ( ‘c‘ ) co = coroutine.create ( function () print ( ‘coroutine yielding‘ ) c.callback( function () coroutine.yield () end ) print ( ‘coroutine resumed‘ ) end ) coroutine.resume (co) coroutine.resume (co) print ( ‘the end‘ ) |
1
|
local c = require ‘c‘ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include<stdio.h> #include<stdlib.h> #include<lua.h> #include<lualib.h> #include<lauxlib.h> static int c_callback(lua_State *L){ int ret = lua_pcall(L, 0, 0, 0); if (ret){ fprintf (stderr, "Error: %s\n" , lua_tostring(L, -1)); lua_pop(L, 1); exit (1); } return 0; } static const luaL_Reg c[] = { { "callback" , c_callback}, {NULL, NULL} }; LUALIB_API int luaopen_c (lua_State *L) { luaL_register(L, "c" , c); return 1; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include<stdio.h> #include<stdlib.h> #define LUA_LIB /* 告诉Lua,这是一个LIB文件 */ #include<lua.h> #include<lualib.h> #include<lauxlib.h> static int c_cont(lua_State *L) { /* 这里什么都不用做:因为你的原函数里面就没做什么 */ return 0; } static int c_callback(lua_State *L){ /* 使用 lua_pcallk,而不是lua_pcall */ int ret = lua_pcallk(L, 0, 0, 0, 0, c_cont); if (ret) { fprintf (stderr, "Error: %s\n" , lua_tostring(L, -1)); lua_pop(L, 1); exit (1); } /* 因为你这里什么都没做,所以c_cont里面才什么都没有。如果这里需要做 * 什么东西,将所有内容挪到c_cont里面去,然后在这里简单地调用 * return c_cont(L); * 即可。 */ return 0; } static const luaL_Reg c[] = { { "callback" , c_callback}, {NULL, NULL} }; LUALIB_API int luaopen_c (lua_State *L) { /* 使用新的 luaL_newlib 函数 */ luaL_newlib(L, c); return 1; } |
1
2
3
4
|
lua -- co.lua coroutine yielding coroutine resumed the end |
1
2
3
4
5
6
7
8
9
|
function do_login(server) server:login( function (data) -- 错误处理先不管,假设有一个全局处理错误的机制(后面会提到,实际 -- 上就是newtry/protect机制) server:get_player_info( function (data) player:move_to(data.x, data.y) end ) end , "username" , "password" ) end |
1
2
3
4
5
|
function d_login(server) server:login( "username" , "password" ) local data = server:get_player_info() player:move_to(data.x, data.y) end |
1
2
3
4
5
6
7
8
9
10
11
|
local current function server:login(name, password) assert ( not current, "already send login message!" ) server:callback_login( function (data) local cur = current current = nil coroutine.resume (cur, data) end , name, password) current = coroutine.running () coroutine.yield () end |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function coroutinize(f, reenter_errmsg) local current return function (...) assert ( not current, reenter_errmsg) f( function (...) local cur = current current = nil coroutine.resume (cur, ...) end , ...) current = coroutine.running () coroutine.yield () end end |
2. 幽灵一般的 nil
1
2
3
4
5
6
7
|
undefined = {} -- 指定一个全局变量存在,但不指向任何地方: a = undefined -- 判断这个全局变量是否不指向任何地方: if a == undefine then ... end -- 彻底删除这个变量: a = nil |
3. 没有continue
1
2
|
local a = true repeat a = false until a |
1
2
3
4
5
6
7
|
for line in configfile do if string.sub (line, 1 , 1 ) == ‘#‘ then goto next end parse_config(line) :: next :: end |
1
2
3
4
5
6
7
8
9
10
|
local i repeat if i == 5 then goto next end local j = i * i print ( "i = " ..i.. ", j = " ..j) i = i + 1 :: next :: until i == 10 |
1
2
3
4
|
lua -- "noname\2013-01-03-1.lua" lua: noname\2013-01-03-1.lua:10: <goto next> at line 4 jumps into the scope of local ‘j‘ shell returned 1 Hit any key to close this window... |
5 年前
关于协程,我当然知道协程该怎么用。Lua C API 确实有些细节上不太清楚,文档太简略了。pcallk 只能解决一次 yield 吧?如果 yield 的次数不定该怎么办?我有个库的函数,在运行过程中可能需要调用一个回调函数来取某些数据。在 Lua 绑定中,这个回调函数就是调用一个 Lua 函数,然后由于涉及网络操作,它是会 yield 不定次数的。
你说的所有这些,要么是 LuaJIT 2.0.0 还没实现的特性(LuaJIT 比 Lua 快太多了),要么是要求作者对 Lua 该怎么编程很熟悉(如果我接手的那些代码是你写的就好了)。至于表达能力,还是不要太强的好,不然每个人的错误返回方式和面向对象的实现都不一样,概念是统一了,实现千差万别、各不相容。
没错,「do return end」就是调试时用的。
5 年前
@依云: 恩,说句实话,只看reference的确很难搞明白k系列函数内部的核心思想。我是一开始就跟着邮件列表的讨论才比较清楚的。不过你真的可以看看novelties-5.2.pdf,这里面有很详细的说明。
另外不明白“一次yield”和“多次yield”有什么区别。只要用了k系列函数,你多少yield都没问题的,因为Lua自己会帮你维护Lua内部yield时候的状态。无论你如何yield,回到C层面(即从内部的coroutine返回)只会有一次,因此k系列函数一定能做到你想要的,而且并不需要特别的设计。
你仔细看看LuaJIT,很多特性已经实现了,包括goto。k系列函数没实现是基于两个原因:1.LuaJIT关注纯Lua应用,甚至用ffi库取代了C API的必要性;2.LuaJIT因为与Lua作者的巨大分歧(邮件里面吵了好几架),所以不打算实现5.2兼容了。至少短期内是不想的。sigh……快的话,其实快不了多少,只是科学计算方面的确快了很多,如果你的代码是C模块密集的,那么LuaJIT很难提高效率,其次是如果你用了NYI的特性,那么也是不会快的(比如字符串模式匹配和coroutine),从我的经验看,网游逻辑书写用luaJIT对效率的提升不大,甚至可能比原Lua更慢。
表达能力问题的确是个双刃剑,但有个朋友说得好“做得到总比做不到好”,这个就看怎么解读了。
错误返回是有标准模式的,文章里面提到了newtry/protect模式,不过写到后来写忘了= =有时间补上吧,OO的话也是有标准模式的,而且是两套。关键是,因为底层概念统一,所以即使是千差万别的实现,最终也一定是兼容的。你如果处理过实现之间的纠葛就会体会到底层概念统一带来的巨大好处。
do return end的话也就是一个词和三个词的区别吧……sigh……就当多打字了,实在不行做个imap或者iab呗……
5 年前
哇,偶像你也在这里!!
5 年前
newtry/protect??
luasocket中的那套,我当时看了也觉得蛮有意思的
可否补充介绍下实际过程的使用方式
5 年前
你好,能给我lua邮件列表的邮箱吗?谢谢~~
4 年前
您好,想问下,如果想在pcall里使用coroutine,有什么办法嘛? 文中的protect,指的就是luaSocket里那种封装pcall的方式吧
4 年前
我觉得 lua 还有一个坑爹特性,table 当哈希表时,无法以 O(1) 的时间复杂度取得其元素个数。
4 年前
从lily那里过来的。看了你这文章,受益颇多。
4 年前
求解释coroutinize中这部分的必要性
local cur = current
current = nil
3 年前
local a = true
repeat a = false until a == false
这样吧
3 年前
关于continue的例子,我猜楼主的意思是这样吧?
少写了一个local?
local a = true
repeat
local a = false
until a