协程
协程知识点梳理
协程就是一个微线程,涉及迭代器的知识点和知识模型
[0]*5 = [0,0,0,0,0]
用yield实现斐波拉契数列:
1 def fibs(n): 2 index = 0 3 a = 0 4 b = 1 5 while index<n: 6 yield b 7 a, b = b, a + b 8 index += 1 9 print(fibs(10)) 10 address = fibs(20) 11 print(address.__next__()) 12 print(address.__next__()) 13 print(address.__next__()) 14 ############运行结果:########### 15 <generator object fibs at 0xb71c853c> 16 1 17 1 18 2
第一次执行__init__()时,会执行到yield处,第二次调用时再向后执行,到yield处再次返回并阻塞。
用yield和send实现信息的传递:
1 def fibs(n): 2 index = 0 3 a = 0 4 b = 1 5 while index < n: 6 c = yield b # 这里yield之后会执行send(),所以会将send()的内容传给c 7 print('task.......{}'.format(c)) 8 a, b = b, a + b 9 index += 1 10 11 12 address = fibs(6) 13 N = address.send(None) 14 i = 0 15 while True: 16 print('外部send发送', N) 17 try: 18 N = address.send(i) # 返回yield的内容给N 19 i += 1 20 except StopIteration: 21 print('生成器已空') 22 break
运行结果:
协程主要的两个库:greenlet库 和 gevent库
greenlet库
greenlet 是底层实现了原生协程的C扩展库,它需要用使用使用 switch 方法实现协程的作用(转换)
1 from greenlet import greenlet 2 import random 3 import time 4 def producer(): 5 while True: 6 item = random.randint(0, 99) 7 print('生产了%s'%item) 8 c.switch(item) # 切换到消费者,并将item传入消费者 9 time.sleep(1) 10 11 def consumer(): 12 while True: 13 item = p.switch() # 切换到生产者,并等待生产者传入item 14 print('消费了%s'%item) 15 16 c = greenlet(consumer) # 将一个普通函数变成协程 17 p = greenlet(producer) 18 c.switch() # 让消费者先进入暂停状态(只有恢复时才能接收数据) 19 ######部分运行结果:######### 20 生产了52 21 消费了52 22 生产了96 23 消费了96 24 生产了45 25 消费了45
greenlet 的价值有:高性能的原生协程;语义更加明确的显示切换;直接将函数包装成协程,保持代码的原有
风格。
gevent库
gevent 库通过封装了 libev(基于epoll) 和 greenlet 两个库。给我们做好了封装,允许我们以类似于线程的方式
使用协程。这样我们几乎不用重写原来的代码就能充分利用epoll 和 协程威力。
它的价值主要是:使用epoll 的 libev 来避开阻塞;使用基于gevent 的高效协程来切换执行;只在遇到阻塞的时
候切换,没有轮换的开销,也没有线程的开销。下面的代码是用gevent实现并发服务器:
1 import gevent 2 # 将Python 内置的socket直接换成封装了IO多路复用的socket 3 from gevent import monkey; monkey.patch_socket() 4 import socket 5 server = socket.socket() 6 server.bind(('', 12345)) 7 server.listen(1024) 8 def worker_coroutine(connection): # 工作协程的内容 9 while True: 10 recv_data = connection.recv(1024) 11 if recv_data: 12 print(recv_data) 13 connection.send(recv_data) 14 else: 15 connection.close() 16 break 17 18 while True: 19 connection, remote_address = server.accept() 20 # 生成一个协程,并将connection作为参数传入 21 gevent.spawn(worker_coroutine, connection)
这个服务器可以同时为多个客户端服务!
gevent 协程通信
由于gevent 基于greenlet, 所以是可以通过switch 通信;因为gevent 不需要我们使用手动切换,因此我们不会去使
用switch !
1 from gevent import monkey; monkey.patch_all() 2 import gevent 3 from gevent.queue import Queue 4 import random 5 queue = Queue(3) 6 def producer(queue): 7 while True: 8 item = random.randint(0, 99) 9 print('生产了%s' % item) 10 queue.put(item) 11 12 def consumer(queue): 13 while True: 14 item = queue.get() 15 print('消费了{}'.format(item)) 16 17 p = gevent.spawn(producer, queue) # 把函数封装成协程,并开始调度 18 c = gevent.spawn(consumer, queue) 19 # gevent.sleep(1) 20 gevent.joinall([p, c])
清澈的爱,只为中国