协程

协程知识点梳理

协程就是一个微线程,涉及迭代器的知识点和知识模型

[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])

 

 

 

 

 

 

 

 

 

 

posted @ 2018-04-03 15:23  巴蜀秀才  阅读(185)  评论(0编辑  收藏  举报