python—协程

协程的概念:

协程: 协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。

而协程是非抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。

协程主解决的是IO的操作。

协程,又称微线程,纤程。英文名Coroutine。

协程的优点:

优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,

因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,

在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

因为协程是一个线程执行,那怎么利用多核CPU呢?简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

yield的简单实现

yield的功能类似于return,但是不同之处在于它返回的是生成器。

生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭代器不一定是生成器)。

如果一个函数包含yield关键字,这个函数就会变成一个生成器。

生成器并不会一次返回所有的结果,而是每次遇到yield关键字后返回相应的结果,并保留函数当前的运行状态,等待下一次的调用。

由于生成器也是一个迭代器,那么他就应该支持next方法来获取下一个值。

import time
def consumer(name):
    print("消费者准备吃包子-----------")
    while  True:
        new_baozi  = yield  #接受调用生成器的值
        print("%s吃第%d个包子"%(name,new_baozi))
        time.sleep(1)

def producer(name):
    r=con.__next__()
    r=con2.__next__()

    count=1
    while True:
        print("%s正在生产f第%s个包子和第%d个包子"%(name,count,count+1))
        #调用生成器并且发送数据
        con.send(count)
        con2.send(count+1)
        count+=2
if __name__ == '__main__':
    con=consumer("翠花")
    con2=consumer("王大锤")

greenlet 模块

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

安装 :pip3 install greenlet。如果引入的时候还是报错,使用pycharm进行下载


from greenlet  import greenlet
def work1():
  print(12)
  gr2.switch()
  print(45)
  gr2.switch()


def work2():
    print(89)
    gr1.switch()
    print(43)


#  1.将要执行的函数封装到greenlet对象中
gr1 = greenlet(work1)
gr2 = greenlet(work2)
#   2.想先执行那个函数就可以使用    对象,swith()方法进行执行
gr1.switch()

结果:

12
89
45
43

gevent模块

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,

在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。

Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

import time
import  requests
import gevent
def f(url):
    print("get",url)
    resp=requests.get(url)
    data = resp.text
    print("%d bytes received  from %s"%(len(data),url))


# 1.普通模式
s=time.time()
f("http://www.langlang2017.com/img/banner1.png")
f("http://www.langlang2017.com/img/banner2.png")
f("http://www.langlang2017.com/img/banner3.png")
f("http://www.langlang2017.com/img/banner4.png")
e=time.time()
print("普通模式时间",e-s)


#   2.使用gevent模块
start = time.time()
gevent.joinall([gevent.spawn(f,"http://www.langlang2017.com/img/banner1.png"),
                gevent.spawn(f, "http://www.langlang2017.com/img/banner2.png"),
                gevent.spawn(f, "http://www.langlang2017.com/img/banner3.png"),
                gevent.spawn(f, "http://www.langlang2017.com/img/banner4.png")])#创建一个普通的greenlet对象并切换

print("gevent时间",time.time()-start)
"""
如果量小的话串行比gevent
"""

结果:

get http://www.langlang2017.com/img/banner1.png
396098 bytes received  from http://www.langlang2017.com/img/banner1.png
get http://www.langlang2017.com/img/banner2.png
666248 bytes received  from http://www.langlang2017.com/img/banner2.png
get http://www.langlang2017.com/img/banner3.png
665348 bytes received  from http://www.langlang2017.com/img/banner3.png
get http://www.langlang2017.com/img/banner4.png
755635 bytes received  from http://www.langlang2017.com/img/banner4.png
普通模式时间 34.99790930747986
get http://www.langlang2017.com/img/banner1.png
396098 bytes received  from http://www.langlang2017.com/img/banner1.png
get http://www.langlang2017.com/img/banner2.png
666248 bytes received  from http://www.langlang2017.com/img/banner2.png
get http://www.langlang2017.com/img/banner3.png
665348 bytes received  from http://www.langlang2017.com/img/banner3.png
get http://www.langlang2017.com/img/banner4.png
755635 bytes received  from http://www.langlang2017.com/img/banner4.png
gevent时间 32.77941083908081

如果量小的话串行比gevent模块花费时间可能少
如果大量数据就能显示出他的性能了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章