A coroutine is similar to a thread (in the sense of multithreading): it is a line
of execution, with its own stack, its own local variables, and its own instruction
pointer; but it shares global variables and mostly anything else with other
coroutines. The main difference between threads and coroutines is that, conceptually
(or literally, in a multiprocessor machine), a program with threads runs
several threads in parallel. Coroutines, on the other hand, are collaborative:
at any given time, a program with coroutines is running only one of its coroutines,
and this running coroutine suspends its execution only when it explicitly
requests to be suspended.
9.1 Coroutine Basics
Lua packs all its coroutine-related functions in the coroutinetable. The create (table 里面的一个function)
function creates new coroutines. It has a single argument ,接受一个函数作参数,返回thread type, . which
represents the new coroutine. Often, the argument to create is an anonymous function, like here:
co = coroutine.create(function () print("hi") end)
print(co) --> thread: 0x8071d98
A coroutine can be in one of four states: suspended, running, dead, and normal.
print(coroutine.status(co)) --> suspended,并没有通过属性,而是通过方法 coroutine.status(),也是table's method
When we create a coroutine, it starts in the suspended state; a coroutine does
not run its body automatically when we create it. Function coroutine.resume
(re)starts the execution of a coroutine, changing its state from suspended to
running:
coroutine.resume(co) --> hi
print(coroutine.status(co)) --> dead
The real power of coroutines[ stems from :源于 ] the yield function,
----Lession7.lua
co=coroutine.create(function ()
for i=1,5 do
print (i);
coroutine.yield(co);
end
end);
> dofile('lession7.lua');
> =coroutine.status(co);
suspended
> coroutine.resume(co);
1
> coroutine.resume(co);
2
> coroutine.resume(co);
3
> coroutine.resume(co);
4
> coroutine.resume(co);
5
> =coroutine.status(co);
suspended
> coroutine.resume(co);
> =coroutine.status(co);
dead
> print(coroutine.resume(co));
false cannot resume dead coroutine --when try to resume a dead coroutine, the function will return false,plus a error message.
Note that resume runs in protected mode. Therefore, if there is any error inside
a coroutine, Lua will not show the error message, but instead will return it to
the resume call.
When a coroutine resumes another, it is not suspended; can not resume it. it is not running either, its own status is what we call the normal state.
A useful facility in Lua is that a pair resume–yield can exchange data.The
first resume, which has no corresponding yield waiting for it, passes its extra
arguments as arguments to the coroutine main function:
=========================================
co=coroutine.create(
function (a,b)
print(a+5,b+4);
end
);
print(coroutine.resume(co,1,3));-->
dofile('lession7.lua');
6 7
true --可以看到resume 返回了true.
================================
co=coroutine.create(
function (a,b)
print(a+5,b+4);
return 5,8 -- add return statement
end
);
print(coroutine.resume(co,1,3));
> dofile('lession7.lua');
6 7
true 5 8 -- also return the function's result.
==========================
co=coroutine.create(
function (a,b)
print(a+5,b+4);
coroutine.yield(co,a+10,b+9); -- yield the coroutine ,pass argument
end
);
print(coroutine.resume(co,1,3));
> dofile('lession7.lua');
6 7
true thread: 01858258 11 12 --可以看到返回了传入 yield 的parameter. the return result is a thread to resume,yield 之所以会返回thread,是因为我们传进去的, 如果只是 coroutine.yield(a+10,b+9); ,那就不返回thread,
that's yield 什么resume 的就返回什么
==============================
co=coroutine.create(
function (a,b)
print("co1 ->",a+5,b+4);
print("co2-->",coroutine.yield(a+1,b+1));
end
);
print(coroutine.resume(co,1,3));
print(coroutine.resume(co,2,2));
> dofile('lession7.lua');
co1 -> 6 7
true 2 4 --第一个resume,返回是出入yield的参数,此时不在继续执行,print 2 no excute,untile the next resume
co2--> 2 2 --当再次resume,将从断点开始,此时的yield 将是返回传入resume的参数,而不再是a+1,b+1, that is 3,3
true
>
==============================
it is important to clarify some concepts before we go on. Lua offers what we call asymmetric coroutines. This means that it has a function to suspend the execution of a coroutine and a different function to resume a suspended coroutine.
other languages offer symmetric coroutines, where there is only one function to transfer control from any coroutine to another.
Some people call asymmetric coroutine semi-coroutines (being not symmetrical, they are not really co). However, other people use the same term semicoroutine to denote a restricted implementation of coroutines, where a coroutine can suspend its execution only when it is not calling any function, that is, when it has no pending calls in its control stack. In other words, only the main body of such semi-coroutines can yield. A generator in Python is an example of this meaning of semi-coroutines. (暂时无法理解,可能需要学习python后才能有深刻的体会,但目前我们只要知道上面描述的特性就可以了,,how to create a routine,resume it, yiled it,and how then pass the parameter)
9.2 Pipes and Filters
function producer ()
while true do
local x = io.read() -- produce new value
send(x) -- send it to consumer
end
end
function consumer ()
while true do
local x = receive() -- receive value from producer
io.write(x, "\n") -- consume it
end
end
The problem here is how to match send with receive. It is a typical instance of
the “who-has-the-main-loop” problem.
Coroutines provide an ideal tool to match producers and consumers, because a resume–yield pair turns upside-down the typical relationship between caller and callee. When a coroutine calls yield, it does not enter into a new function; instead, it returns a pending call (to resume). Similarly, a call to resume does not
start a new function, but returns a call to yield.
function receive ()
local status, value = coroutine.resume(producer)
return value
end
function send (x)
coroutine.yield(x)
end
Of course, the producer must now be a coroutine:
producer = coroutine.create(
function ()
while true do
local x = io.read() -- produce new value
send(x)
end
end)
------------我实现的版本
local receive;
local send; --predefine the receive and send. orelse the in the consumer function will not see the receive function
local producter=coroutine.create(
function ()
while true do
x= io.read();
if x then
send(x);
end
end
end
);
local function consumer()
f=io.open("testWrite.txt",'w');
while true do
x=receive();
if x then
f:write(x,'\n');
f:flush();
end
end
--io.close(f);
f.close(f);
end;
receive=function ()
local statue, thread, value =coroutine.resume(producter); --because in the send function, I pass the coroutine to the yield function, I need 3 variable to accept the return value.
return value;
end
send= function (p)
coroutine.yield(producter,p);
end
consumer(); --let consumer to resume the producter.
从上面的实现我们可以看到resume-yield pairs 的强大之处是在于他能从yield 断点继续执行, 其实producter-consumer 的类似功能我们可以通过while/for goto 实现,但可能是call 多次funciton,但coroutine run in single thread,a single funciton. no need to call one function multiple time.
Another way to write the
program is to use a producer-driven design, where the consumer is the coroutine.
----------
local send;
local receive;
local producter=function()
while true do
x=io.read();
send(x);
if x=='q' then
break;
end;
end
end
local cosumer=coroutine.create(function(x)
fs=io.open("testWrite.txt",'w');
fs:write(x,'\n');--the first resume will get the paramer here. next time, the parameter is return from the yield, and the function will never run to here again.
while true do
x=receive();
fs:write(x,'\n');
if x=='q' then
break;
end
end
print('end the consumer');
fs:flush();
fs:close();
end);
send=function(x)
coroutine.resume(cosumer,x);
end
receive=function()
return coroutine.yield(); [生产出value,放弃当前的执行权限,所以说这个yield 是很合适做这个函数的]
end
producter();
--------------把consumer 作为coroutine is the best choice
We can extend this design with filters, which are tasks that sit between the
producer and the consumer doing some kind of transformation in the data. A
filter is a consumer and a producer at the same time, so it resumes a producer
to get new values and yields [生产的意思,,] the transformed values to a consumer.
filter 其实无非是多了一个处理get 到的value的函数,之所以分开一个函数写,其实是为了保持consumer and producter的独立性,怎么处理给另外一个函数,,重用性的要求,,,
coroutines are a kind of (non-preemptive) multithreading.
With pipes, each task runs in a separate process; with coroutines, each task runs
in a separate coroutine. Pipes provide a buffer between the writer (producer)
and the reader (consumer) so there is some freedom in their relative speeds.
9.3 Coroutines as Iterators
书中提供了一个比较复杂的例子,就是计算一个数组的所以可能排列组合
function permgen (a, n)
n = n or #a -- default for 'n' is size of 'a'
print('enter permgen##########'..n);
if n <= 1 then -- nothing to change?
print("enter printResult"..n);
printResult(a)
else
for i = 1, n do
-- put i-th element as the last one
a[n], a[i] = a[i], a[n]
print("1debug.."..i.."-->"..n);
io.write("debug..");
for b=1,#a do
io.write(a[b], " ")
end
io.write("\n")
-- generate all permutations of the other elements
permgen(a, n - 1)
-- restore i-th element
a[n], a[i] = a[i], a[n]
io.write("restore debug..");
for k=1,#a do
io.write(a[k], " ")
end
io.write("\n")
end
end
end
function printResult (a)
print("---------start====");
for i = 1, #a do
io.write(a[i], " ")
end
io.write("\n")
print("---------end====");
end
----我也是加了些打印信息才大概猜想到他的思路,但有待更清晰的想法,所以这里待续,,,暂时不理会具体的算法。
把关注点放到coroutine iterator 上来,,
function permgen (a, n)
n = n or #a
if n <= 1 then
coroutine.yield(a) --这里利用yield,把排出的一个组合结果返回到resume.
else
<as before>
----iteraotr factor ,产生一个iterator function.他将 table a, co (coroutine)wrap 进闭包的evn.同时,然后在循环调用
iterator 时resume co,至道排出一种组合,yield 出结果.
function permutations (a)
local co = coroutine.create(function () permgen(a) end)
return function () -- iterator
local code, res = coroutine.resume(co)
return res
end
end
for p in permutations{"a", "b", "c"} do
printResult(p)
end
The permutations function uses a common pattern in Lua, which packs a
call to resume with its corresponding coroutine inside a function,也就是这个设计模式,包了coroutine and resume 到一个函数中, This pattern is so common that Lua provides a special function for it: coroutine.wrap. Like create, wrap creates a new coroutine. Unlike create, wrap does not return the coroutine itself; instead, it returns a function that, when called, resumes the coroutine.(其实就是跟permutations 的功能一样) Unlike the original resume, that function does not return an error code as its first result; instead, it raises the error in case of error.
Using wrap, we can write permutations as follows:
function permutations (a)
return coroutine.wrap(function () permgen(a) end)
end
9.4 Non-Preemptive Multithreading [非抢占,,]
As we saw earlier, coroutines allow a kind of collaborative multithreading. Each
coroutine is equivalent to a thread. A pair yield–resume switches control from
one thread to another. However, unlike regular multithreading, coroutines are
non-preemptive. While a coroutine is running, it cannot be stopped from the
outside. It suspends execution only when it explicitly requests so (through a
call to yield).