协程
问题一: 生成器与函数的区别?
生成器分阶段的返回多个值,相当于有多个出口(结果); yield
''' yield # 中断、返回函数值 1、只能在函数中使用 2、会暂停函数执行并且返回表达式结果 3、一次只能返回一个值 yield 取值 next() ''' def func(): print(1) yield 'a' print(2) yield 'b' print(3) g = func() print(next(g)) g.__next__()
问题二: 协程与生成器的区别?
有多个出口,同时可以有多个入口
def func(): a = (yield ) print(a) b = (yield ) print(b) # c = (yield ) # print(c) g = func() g.send(None) # 第一次发送必须是None 或者使用 next(g) 对应的 a,发送的是个空 print(g.send(1)) # 对应的是b print(g.send(2)) # 到这里就停止了。 StopIteration, 因为后面没有了
问题三: 协程算并发嘛?
严格来说 不算。如果有一个地方卡住了, 会一直卡
问题四: 协程的意义?
最主要"配合io多路复用使用",当前的意义就是切换使用,耗费资源小
生产者/消费者模型
import random, time # 生产者 def produce(consumer): # 参数(生成器) next(consumer) while True: item = random.randint(0, 99) # 随机生成一个数 print("生产者生产了%s" % item) consumer.send(item) # 把item给消费者 time.sleep(2) def consumer(): # 消费者 才是一个协程(生成器) while True: item = (yield ) print("消费者消费了%s " % item) c = consumer() produce(c)
greenlet
问题一: 什么是greenlet ?
虽然CPython(标准Python)能够通过生成器来实现协程,
但使用起来还并不是很方便。
与此同时,Python的一个衍生版 Stackless Python
实现了原生的协程,它更利于使用。
于是,大家开始将 Stackless 中关于协程的代码
单独拿出来做成了CPython的扩展包。
这就是 greenlet 的由来,因此 greenlet 是底层实现了原生协程的 C扩展库。
说白了就是协程
问题二: 如何使用 greenlet ?
from greenlet import greenlet import random, time # 生产者 def produce(): # 参数(生成器) while True: item = random.randint(0, 99) # 随机生成一个数 print("生产者生产了%s" % item) c.switch(item) # 切换到消费者,并将item传入消费者 time.sleep(2) def consumer(): # 消费者 才是一个协程(生成器) while True: item = p.switch() # 切换到消费者,并等待消费者传入item print("消费者消费了%s " % item) p = greenlet(produce) # 把函数包装成一个协程 c = greenlet(consumer) c.switch() # 启动
问题三: 为什么需要greenlet ?
greenlet 的价值
价值一: 高性能的原生协程
价值二: 语义更加明确的显式切换
价值二: 语义更加明确的显式切换
gevent协程
问题一: 什么是 gevent ?
sudo pip3 install gevent
虽然,我们有了 基于 epoll 的回调式编程模式,但是却难以使用。
即使我们可以通过配合 生成器协程 进行复杂的封装,以简化编程难度。 但是仍然有一个大的问题: 封装难度大,现有代码几乎完全要重写
gevent,通过封装了 libev(基于epoll) 和 greenlet 两个库。 帮我们做好封装,允许我们以类似于线程的方式使用协程。
以至于我们几乎不用重写原来的代码就能充分利用 epoll 和 协程 威力。
问题二: gevent 的价值是什么 ?
遇到阻塞就切换到
另一个协程继续执行 !
价值一: 使用基于 epoll 的 libev 来避开阻塞
价值二: 使用基于 gevent 的 高效协程 来切换执行
价值三: 只在遇到阻塞的时候切换,
没有轮需的开销,也没有线程的开销
问题三: 如何使用 gevent ?
g
e
v
e
n
t
并
发
服
务
器
from gevent import monkey;monkey.patch_socket() import gevent import socket server = socket.socket() server.bind(('0.0.0.0', 8888)) server.listen(1000) def workon(conn): while True: data = conn.recv(1024) if data: print(data.decode()) conn.send(data) else: conn.close() break while True: conn, addr = server.accept() # Thread(target=workon, args=(conn,)).start() # 多线程的方法 gevent.spawn(workon, conn)
gevent 协程通信
gevent通信 问题引入
问题一: 协程之间不是能通过switch通信嘛?
是的,由于 gevent 基于 greenlet,所以可以。
问题二: 那为什么还要考虑通信问题?
因为 gevent 不需要我们使用手动切换,
而是遇到阻塞就切换,因此我们不会去使用switch !
gevent.queue.Queue
from gevent import monkey;monkey.patch_all() import gevent from gevent.queue import Queue import random ''' gevent.queue.Queue ''' q = Queue(3) # 生产者 def produce(q): while True: item = random.randint(0, 99) print("生产者生产了%s" % item) q.put(item) gevent.sleep(1) def consumer(q): while True: item = q.get() print("消费者消费了%s" % item) gevent.sleep(1) p = gevent.spawn(produce, q) # 将函数封装成协程,并开始调度 c = gevent.spawn(consumer, q) # c.switch() # 开启(不使用) gevent.joinall([p, c]) # 阻塞(一阻塞就切换协程)等待
测试用客户端:
import socket client = socket.socket() #创建一个套接字 client.connect( ('127.0.0.1',8888 ) ) #连接服务端 while True: data = input('请输入你要发送的数据:') data = data.encode() client.send(data) print('接收到的数据:', client.recv(1024).decode())
协程整理完毕。
作者:含笑半步颠√
博客链接:https://www.cnblogs.com/lixy-88428977
声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,欢迎转载。转载与引用请注明作者及出处。