zoukankan      html  css  js  c++  java
  • 关于协程的理解

    参考文档:http://www.cnblogs.com/coderzh/articles/1202040.html(我觉得这个作者讲的很好,让我茅塞顿开,所以借鉴了不少,如有侵权嫌疑请私戳哦)

                      廖雪峰python官方文档

    1、浅层

    def h():
        print('Wen Chuan')
        yield 5
        print('Fighting!')
    
    c = h()
    next(c)
    

      输出:

           这里的第一个next,只输出了yield之前的东西。

           程序从c=h()开始,然后进入next语句,然后启动generator h(),打印第一句"Wen chuan",然后进入yield,往generator生成一个数据5,然后也没有退出generator之类的语句,所以卡在这了,如果在来一个next,则可以全部输出:

          为啥没有输出5?当我们再次调用next时,会继续执行,直到找到下一个yield表达式。由于后面没有yield了,因此会拋出异常。从上面这个例子也可验证next等价于send(None)这个道理。

    2、深入

     1 def h():
     2     print('Wen Chuan')
     3     mm = yield 5  # Fighting!
     4     print("mm=",m)
     5     dd = yield 12
     6     print('We are together!   dd=',dd)
     7 
     8 c = h()
     9 m=next(c)  #相当于c.send(None)
    10 d=c.send('Fighting!')  #(yield 5)表达式被赋予了'Fighting!'
    11 print(m,d)
    12 a=next(c)
    13 print('error',a)

           过程:

           1.c=h(),程序开始

           2.m=next(c)启动生成器

           3.进入生成器h(),一直扫描直到遇到yield,所以打印了‘Wen chuan’,此时遇到yield 5,将5压进生成器,因为第三行代码yield在等式右边,所以返回generator的参数,也就是5,赋值给mm(这里我总是弄混到底mm是甚麽),此时“子程序”停留在第三行

           4.回到“主程序”第9行,因为将next获得的值赋给m,所以此时刚才进栈的5再出栈给m,接着运行第10行(send作用和next相似,但是send可以传递yield表达式的值,如果h()是需要传参的,就用send传)因为是send,所以又去“子程序”运行,原本“子程序”运行到第三行,将send携带的参数传递给等式左边,即mm=Fighting!,接着第四行打印mm,接着运行,遇到yield 12,将12压进栈,此时“子程序”停留在第5行等式右边

           5.回到“主程序”第10行,send从生成器出栈12复制给d,接着运行11行打印5,12,然后12行next又去“子程序”找yield,回到子程序第5行,因为next不能带参数,所以这个yield表达式没有值,所以dd=None,然后打印第6行,然后h()已经运行完毕,没有找到yield,报错,然后就不会返回到“主程序了”,接下来的13行也没有打印。

    3、理解

    1)

     1 def h():
     2     print('Wen Chuan')
     3     mm = yield 5  # Fighting!
     4     print("mm=",m)
     5     dd = yield 12
     6     print('We are together!   dd=',dd)
     7 
     8 c = h()
     9 m=next(c)  #相当于c.send(None)
    10 d=c.send('Fighting!')  #(yield 5)表达式被赋予了'Fighting!'
    11 print(m,d)
    12 13 print('error')

            这里,没有报错,而且执行了13行没有执行第6行:在第10行send之后执行到“子程序”中的第5行,就不往下执行了,然后返回到第10行,接着把“主程序”执行完。而上一个例子报错是又有一个next,所以“子程序”接着往下找把“子程序”找完了也没找到yield,所以第6行执行了,而13行不执行,相当于在“主程序”中第12行遇到一个error直接退出本层循环所以退出运行,所以第13行就没有执行。

    2)

            这里类似“主程序”“子程序”或者多线程的理解来理解协程,但是协程与多线程相比不需要线程切换的开销,与子程序比起来协程可以“子程序”内部中断,再去执行别的“子程序”,有点类似CPU中断。

            上面还有一个问题是为甚麽没有输出yield的5,通过第二个例子的理解,也可看到,第一个例子只有next语句,并不是b=next(c),所以是有返回值5的,只是没有接收。在这里要特别注意b=yield 5,b到底是多少,b的值是send传进来的,而5这个值又被传回给bb=send中的bb。

    3)

             有点全局变量和局部变量的感觉,可以在“子程序”中调用“主程序”的m,但是在“主程序”中不能调用“子程序”的mm,我的理解是h()是一个generator,只有通过yield的数据才能放在这个generator中,同样,要调用的数据也必须是yield过的,这里的mm并不是yield的数据,所以在“主程序”中显示没有这个mm变量

     1 def h():
     2     print('Wen Chuan')
     3     mm = yield 5  
     4     print("mm=",m,mm)#可以调用m
     5     dd = yield 12
     6     print('We are together!   dd=',dd)
     7 
     8 c = h()
     9 m=next(c)  
    10 d=c.send('Fighting!')  
    11 print(m,d)
    12 
    13 print('error',mm)#不能调用mm

    4、关于廖老师网站那个题目的运行过程的理解

     1 def consumer():
     2     r = ''
     3     index=1
     4     while True:
     5         n = yield r
     6         if not n:
     7             return
     8         print('[CONSUMER] Consuming %s...<index=%s>' % (n,index))
     9         r = '200 OK'
    10         index+=2
    11 
    12 def produce(c):
    13     c.send(None)
    14     n = 0
    15     while n < 5:
    16         n = n + 1
    17         print('[PRODUCER] Producing %s...' % n)
    18         r = c.send(n)
    19         print('[PRODUCER] Consumer return: %s' % r)
    20     c.close()
    21 
    22 c = consumer()
    23 produce(c)

     

             1.第22行开始运行程序,第23行produce(c),进入produce函数

             2.进入produce函数,第13行预激generator,进入consumer函数,直到运行到第5行,此时consumer中的这个n值还没有被赋值,yield栈中为‘ ’,再返回produce函数第13行,因为无赋值,接着进入后面

             3.第17行输出Producing 1...,然后第18行将produce的n=1传给consumer,跳转到第5行,将1赋值给consumer中的这个n,接着往下执行,打印Consuming 2...,然后r='200 ok',再进入while true,运行到第5行,此时将r='200 ok'yield进栈,返回到produce函数

             4.返回到produce函数第18行,send取出当前栈中的‘200 ok’赋值给produce这里的r,然后打印Consumeing return :200 ok,然后进入while n<5:接下来循环进行。

             5.直到最后一次,consumer此时在第5行,yield进栈‘200 ok',此时n还没赋值,返回produce函数第18行,然后打印Consumeing return :200 ok,然后退出while n<5循环,然后这里不知道是怎末退出consumer的,从consumer来说,接下来因为第5行没有收到新的数据,所以这里的n值为None,通过if not n可以退出,然后返回到produce第20行,手动关闭生生成器,退出整个程序。然鹅我删掉了close()和if not n,还是可以正常退出。还等学习更多相关知识应该可以彻彻底底的理解协程整个过程吧!

             额外:这里设了一个index可以看到每次produce和consumer互相转换的时候,这个index不受影响,但是在consumer以外的环境调用不了index,这一点比起局部变量来说要舒服多了。

  • 相关阅读:
    Jqgrid demo-史上最强大,没有之一
    围巾的味道慢慢消退,织围巾的人又在何处呢?
    挺水的一门课,发现全系都过了,就自己挂了,这是一种什么样的感觉呢?
    个人感觉对程序员来说,熬夜是青春最大的杀手
    重装系统分区时,发现一个叫LVM的东西,找出来和大家分享
    如何参加开源项目
    和师兄们水平差的不是一丁半点
    华为RH2285安装过程及经验总结
    kilo本地库制作
    Cinder volume 的使用方法
  • 原文地址:https://www.cnblogs.com/rainbowdawn/p/10674476.html
Copyright © 2011-2022 走看看