关闭

chapter 9Coroutines

464人阅读 评论(0) 收藏 举报
分类:

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).

































































 




 


 





 





 















0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1018823次
    • 积分:11068
    • 等级:
    • 排名:第1437名
    • 原创:59篇
    • 转载:608篇
    • 译文:36篇
    • 评论:224条
    最新评论