python3.x Day6 协程

协程:
#定义来自牛人alex博客
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

使用协程的目的:就是为了最大限度使用CPU,把IO操作解耦,提高程序运行速度

协程的好处:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
"原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:
无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

greenlet是封装好了的协程
gevent 是进一步封装了greenlet

greenlet还需要通过greenlet.greenlet(func)生成greenlet类实例,只能通过gr1.swich()方法手动切换方法执行。
gevent通过gevent.spawn(func,args)生成greenlet类实例,通过gevent.joinall([])装载Greenlet实例,即可启动各个方法,并实现自动的遇到IO就切换
一般在整个当前程序前需要from gevent import monkey引入monkey包,这是个补丁,里边有monkey.patch_all()方法,是明确标记所有IO操作,遇到就切换,
主要是像socket、urllib中的IO操作不会被gevent直接发现时使用这个补丁就能发现了。
gevent需要自行安装pip install gevent

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 一个协程遇到IO操作自动切换到其它协程



源生的协程
import time
import queue
def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)
 
def producer():
 
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n < 5:
        n +=1
        con.send(n)
        con2.send(n)
        print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
 
 
if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

利用模块greenlet写协程,简单一些了

#用greenlet来写个简单的协程,大家猜一下运行结果

import greenlet  #导入greenlet协程包


def green_func1():  #定义第一个协程用的方法
    print(12)
    gr2.switch()  #切换到协程gr2,保存现场,再度切换回来时,从这里开始
    print(34)
    gr2.switch()  #切换到协程gr2,保存现场,再度切换回来时,从这里开始

def green_func2():  #定义第二个协程用的方法
    print(56)
    gr1.switch()  #切换回协程gr1,保存现场,再度切换回来时,从这里开始
    print(78)

gr1=greenlet.greenlet(green_func1)  #定义第一个协程,装入方法green_func1方法,并启动协程gr1
gr2=greenlet.greenlet(green_func2)  #定义第二个协程,装入方法green_func2方法,并启动协程gr2

gr1.switch()  #手动切换到协程gr1开始执行。因为没有执行过,所以切换时,从函数开头执行

来个自动的切换吧,这才是真正的写法:

#写个协程抓个网页看看
import gevent,time  #引入协程模块
from urllib import request
from gevent import monkey #引入协程模块下的monkey补丁模块
monkey.patch_all() #使用monkey模块下的patch_all()方法,作用是:在每个urllib模块的IO操作前增加标记,来明确协程切换时机


def f_get(url):  #定义使用协程的方法,
    print("GET %s"%url)
    resp=request.urlopen(url)
    data=resp.read()
    print("%d bytes recevied from %s"%(len(data),url))

urls=[
    "https://www.baidu.com/",
    "https://github.com/",
    "https://hub.docker.com/",
    "https://www.yahoo.com/"
]

print("同步获取:")
ss_time=time.time()
for u in urls:
    f_get(u)
print("同步耗时:%s"%(time.time()-ss_time))


print("协程异步获取:")
async_time=time.time()
gevent.joinall([    #定义协程的启动,需要传入一个gevent.spawn(其实就是Greenlet类的实例)列表
    gevent.spawn(f_get,"https://www.baidu.com/"),
    gevent.spawn(f_get,"https://github.com/"),
    gevent.spawn(f_get,"https://hub.docker.com/"),
    gevent.spawn(f_get,"https://www.yahoo.com/")
])
print("协程异步耗时:%s"%(time.time()-async_time))


print("循环生成Greenlet实例变成列表协程异步获取:")
gl=[]
for uu in urls:
    gl.append(gevent.spawn(f_get,uu))
async_time=time.time()
gevent.joinall(gl)#定义协程的启动,需要传入一个gevent.spawn(其实就是Greenlet类的实例)列表
print("协程异步耗时:%s"%(time.time()-async_time))

 

posted @ 2017-12-20 15:15  王玥  阅读(193)  评论(0编辑  收藏  举报