协程的概念:
协程: 协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。
而协程是非抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。
协程主解决的是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模块花费时间可能少
如果大量数据就能显示出他的性能了