协程具有极高的执行效率,因为子程序切换不是线程切换,而是由程序控制,
没有线程切换的开销,和多线程比,线程数量越多,性能优势越明显。
因为只有一个线程,不存在同时写变量冲突,在协程中控制共享资源只需要判断状态,
不必加锁,故执行效率比多线程高。
协程是一个线程执行,使用多进程+协程,可充分利用多核CPU,获得极佳性能。
【1】yield实现协程(生产者消费者)
Python通过yield提供了对协程的基本支持,但是不完全。
1 import time 2 3 def consumer(name): 4 print("准备") 5 while True: 6 result = yield 7 print("[%s] 消费 %s" % (name,result)) 8 #time.sleep(1) 9 10 def producer(): 11 12 r = con1.__next__() 13 r = con2.__next__() 14 n = 0 15 while 1: 16 time.sleep(1) 17 print("生产者: %s and %s" %(n,n+1)) 18 con1.send(n) #发送给yield,result得到此值。 19 con2.send(n+1) 20 n +=2 21 22 23 if __name__ == '__main__': 24 con1 = consumer("c1") #获得生成器对象 25 con2 = consumer("c2") 26 p = producer()
【2】Greenlet 协程模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,
它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator。
1 from greenlet import greenlet 2 3 4 def test1(): 5 print(1) 6 gr2.switch() 7 print(3) 8 gr2.switch() 9 10 11 def test2(): 12 print(2) 13 gr1.switch() 14 print(4) 15 16 if __name__ == '__main__': 17 gr1 = greenlet(test1) 18 gr2 = greenlet(test2) 19 gr1.switch()
【3】Gevent
Gevent是一种基于协程的第三方Python网络库,通过greenlet实现协程,非标准库,尚存在缺陷。
在python3中有一个官网正在做并且在3.6中已经稳定的库asyncio。
使用gevent,可以获得极高的并发性能,但gevent只能在Unix/Linux下运行。
由于gevent是基于IO切换的协程,编写的Web App代码,不需要引入gevent的包和修改代码,
只需在部署时,用一个支持gevent的WSGI服务器,就可获得数倍的性能提升。
★过程:
当一个greenlet遇到IO操作时,就自动切换到其他的greenlet,等到IO操作完成,再切换回来。
Gevent直接修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行,这一过程在启动时通过monkey patch(猴子补丁)完成:
1 #例: 2 from gevent import monkey; monkey.patch_socket() 3 import gevent 4 5 def f(n): 6 for i in range(n): 7 print gevent.getcurrent(), i 8 gevent.sleep(1) #sleep模拟IO 9 10 g1 = gevent.spawn(f, 5) 11 g2 = gevent.spawn(f, 5) 12 g3 = gevent.spawn(f, 5) 13 g1.join() 14 g2.join() 15 g3.join() 16 17 #例: 18 from gevent import monkey; monkey.patch_all() 19 import gevent 20 import urllib2 21 import time 22 start=time.time() 23 24 def f(url): 25 print('GET: %s' % url) 26 resp = urllib2.urlopen(url) 27 data = resp.read() 28 print('%d bytes received from %s.' % (len(data), url)) 29 30 gevent.joinall([ 31 gevent.spawn(f, 'https://www.python.org/'), 32 gevent.spawn(f, 'https://www.baidu.com/'), 33 gevent.spawn(f, 'https://github.com/'), 34 ]) 35 36 print("cost time:",time.time()-start)