您的位置:首页 > 编程语言 > Python开发

Python学习笔记 协程

2015-02-04 21:02 211 查看
二十:协程:
协程,又称微线程,纤程,Coroutine
子程序 又称为函数 在所有语言中都是层级调用 A调用B B调用C C返回B B 返回 A
所以子程序调用是通过栈实现的 一个线程就是执行一个子程序
子程序调用总是一个入口 一次返回 调用顺序是明确的 而协程的调用和子程序不同
协程看上去也是子程序 但是执行过程中 在子程序内部可中断 转而执行别的子程序 在适当的时候在返回来接着执行
注意 在一个程序中中断 去执行其他的子程序 不是函数调用 有点类似CPU的中断 比如子程序AB
def A()
print '1'
print '2'
print '3'
def B()
print 'x'
print 'y'
print 'z'
假设由协程执行 在执行过程中 可以随时中断 去执行B B也可能在执行的过程中中断去执行A 结果可能是
1 2 x y 3 z
但是在A中没有调用B 所以协程调用比函数理解更难
看起来像是多线程 但是协程的特点在于是一个线程执行 那和多线程比 协程的优势是:
最大的优势是:协程的执行效率极高,因为子程序切换不是线程切换 而是有程序自身控制,因此 没有线程切换的开销 和多线程比线程数量越多 协程的性能优势越大
其次就是不需要多线程的锁机制 因为只有一个线程
因为协程是一个线程执行 如何利用多核CPU?最简单的方法就是 多进程+协程
Python对协程的支持非常有限 用generator的yield可以一定程度实现协程
生产者-消费者问题 一个线程写消息 一个线程取消息 通过锁机制控制队列和等待 但一不小心就可能锁死
如果改用协程 生产者生产消息 直接通过yield跳转到消费者开始执行 待消费者执行完毕后 切换回生产者继续生产
import time
def consumer():
r=''
while True:
n=yield r
if not n:
return
print('[CONSUMER] consuming %s...' % n)
time.sleep(1)
r='200 OK'

def produce(c):
c.next()
n=0
while n<5:
n=n+1
print('[PRODUCER] producing .%s..' % n)
r=c.send(n)
print('[PRODUCER] consumer return :%s ' % r)
c.close()

if __name__=='__main__':
c=consumer()
produce(c)

结果是 没生产完一个消费一个
注意到consumer函数是以个generator生成器 把一个consumer传入到produce后
1:首先调用 c.next()启动器
2:然后 一旦生产了东西 通过c.send(n) 切换到consumer执行
3:consumer通过yield得到消息 处理 有通过yield把结果返回
4:produce拿到consumer处理的结果 继续生产吓一跳消息
5:produce决定不生产了 通过从c.close()关闭consumer 整个过程结束

2:gevent
Python通过yield提供了对协程的基本支持 但是不完全 而第三方的gevent 为Python提供了比较完善的协程支持
gevent是第三方库 通过greenlet 实现协程 基本思想是:
当一个greenlet 遇到IO操作时 比如访问网络的时候 就自动切换到其他的greenlet 等到IO操作完成 再在适当的时候切换回来继续执行 就保证总有greenlet在运行 而不是等待IO
由于切换是在IO操作中自动完成的 所以gevent需要修改Python自带的一些标准库 这一过程在启动时通过monkey patch 完成
from gevent import monkey;monkey.patch_socket()
import gevent
def f(n):
for i in range(n):
print gevent.getcurrent(),i
g1=gevent.spawn(f,5)
g2=gevent.spawn(f,5)
g3=gevent.spawn(f,5)
g1.join()
g2.join()
g3.join()
运行结果是 。。。。。。0
。。。。。。1
。。。。。。2 34 01234 01234
发现3个greenlet 是一次运行 不是交替运行
要让greenlet 交替运行 通过 gevent.sleep() 交出控制权

def f(n):
for i in range(n):
print gevent.getcurrent(),i
gevent.sleep(0)
会发现是交替运行 000 111 222 333 444 555
而在实际中是不会用gevent.sleep() 去切换协程 而是执行IO操作时 gevent自动切换
from gevent import monkey;monkey.patch_all()
import gevent
import urllib2

def f(url):
print('Get:%s' %url)
resp=urllib2.urlopen(url)
data=resp.read()
print('%d bytes received from %s' %(len(data),url))

gevent.joinall([
gevent.spawn(f,'http://www.baidu.com')
gevent.spawn(f,'http://www.google.com')
gevent.spawn(f,'http://www.yahoo.com')
])

只能在Unix或者Linux上安装运行 windows下面不可以
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: