lua 协程(coroutine)分析
这是一段分析 lua 协程(协同程序,coroutine)的代码,来自 Lua reference manual interface (略有修改):
1function foo (a)
2 print("foo", a)
3 return coroutine.yield(2*a)
4end
5
6co = coroutine.create(function (a,b)
7 print("co-body1", a, b)
8 local r = foo(a+1)
9 print("co-body2", r)
10 local r, s = coroutine.yield(a+b, a-b)
11 print("co-body3", r, s)
12 return b, "end"
13end)
14
15print("1----")
16print("main", coroutine.resume(co, 1, 10))
17print("2----")
18print("main", coroutine.resume(co, "r"))
19print("3----")
20print("main", coroutine.resume(co, "x", "y"))
21print("4----")
22print("main", coroutine.resume(co, "x", "y"))
运行效果如下:
11------
2co-body1 1 10
3foo 2
4main true 4
52------
6co-body2 r
7main true 11 -9
83------
9co-body3 x y
10main true 10 end
114------
12main false cannot resume dead coroutine
这里一共调用了 4 次 resume ,让我们来看看它是怎么运行的。
第一次:
1print("main", coroutine.resume(co, 1, 10))
- 执行
print("co-body1", a, b)
,a 和 b 的值为 resume 提供,a=1, b=10
; - 计算
a+1=2
,进入foo(a)
,同时将刚才的计算结果通过 a 参数传递,执行print("foo", a)
; - 考虑
return coroutine.yield(2*a)
; - 计算
2*a=4
,碰到yield
,挂起foo(a)
调用,将 4 返回给resume
。注意,foo
的return
还没有执行; resume
执行成功,返回true, 4
。
第二次:
1print("main", coroutine.resume(co, "r"))
- 从上一次挂起的
foo(a)
调用开始执行,接着执行没有完成的return
调用; - 因为
yield
返回resume
的调用参数,此时foo(a+1)
返回的值就是字符串"r"
。这里比较难理解。
因为大家可能会顺理成章地认为local r
这个变量的值应该是yield(2*a)
中的2*a
的值。 需要注意的是, yield 的返回值 与 yield 参数的值 是不同的。
前者你可以将其保存在一个变量中,或者 return 它,或者不使用它(不保存 yield 的返回结果);后者则是resume
的返回值。 - 执行
print("co-body2", r)
,r 的值为"r"
; - 考虑
local r, s = coroutine.yield(a+b, a-b)
; - 计算
a+b=11, a-b=-9
,碰到yield
,挂起co
的调用,将 11 和 9 返回给resume
。注意,此时local r, s
的赋值还没有开始。
这里不太好理解的是,为什么a
的值不是"r"
?因为"r"
已经被上面的yield
的返回值给消费掉了。 resume
执行成功,返回true, 11, -9
。
第三次:
1print("main", coroutine.resume(co, "x", "y"))
- 从上一次
yield
的地方开始执行,接着执行没有完成的local r, s =
赋值。上面提到,yield
会返回resume
的调用参数,因此r
和s
的值就是"x"
和"y"
; - 执行
print("co-body3", r, s)
进行打印; - 考虑
return b, "end"
; b
的值一直都是 10 没有变,这里直接返回了,同时返回的还有"end"
这个字符串;- 由于协程函数返回的时候,它的所有返回值都作为
resume
的返回值返回。因此这里的resume
执行成功,返回10, "end"
。
第四次:
1print("main", coroutine.resume(co, "x", "y"))
由于 co
函数已经返回,它处于 dead
状态,不能 resume
,因此第 4 次 resume
失败。
(全文完)
- 文章ID:2225
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/lua-coroutine/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。