协程: 也叫纤程,协程是线程的一种实现,指的是一条线程能够在多任务之间来回切换的一
种实现,对于CPU、操作系统来说,协程并不存在
任务之间的切换会花费时间.目前电脑配置一般线程开到200会阻塞卡顿 .
协程的实现: 协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充
分的利用,
CPU,抢占更多的时间片
一个线程可以由多个协程来实现,协程之间不会产生数据安全问题
协程模块:
greenlet gevent的底层,协程,切换的模块
gevent 直接用的,gevent能提供更全面的功能
(1) 用生成器写生产者消费者模型
# producer 生产者
def producer():
for i in range(10):
yield i
# consumer 消费者
def consumer():
g = producer()
for num in g:
print(num)
consumer()
执行结果:
0
1
2
3
4
5
6
7
8
9
(2) 普通版 greenlet+switch组合 基本实现,只能用switch进行手动切换,
缺陷:不能规避雕io,不能自动实现遇到阻塞就切换.
from greenlet import greenlet
import time
def eat():
print("eat one1")
# 切换到play这个协程中
g2.switch()
time.sleep(1)
print("eat one2")
def play():
print("play one1")
print("play one2")
g1.switch()
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()
执行结果
eat one1
play one1
play one2
eat one2
(3) 升级版 gevent+spawn组合 有缺陷,不能够识别time.sleep()
spawn 自动检测阻塞,遇到阻塞就切换,相当于switch 的升级版
import gevent,time
def eat():
print("eat one1")
time.sleep(1)
print("eat one2")
def play():
print("play one1")
time.sleep(1)
print("play one2")
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
# 协程的阻塞也是join
g1.join() # 阻塞直到g1协程执行完毕
g2.join() # 阻塞直到g2协程执行完毕
执行结果:发现者并不是我们我们希望的结果
eat one1
eat one2
play one1
play one2
(4) 进阶版本 用gevent.sleep() 取代time.sleep()
import gevent
def eat():
print("eat one1")
gevent.sleep(1)
print("eat one2")
def play():
print("play one1")
gevent.sleep(1)
print("play one2")
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
# 协程的阻塞也是join
g1.join() # 阻塞直到g1协程执行完毕
g2.join() # 阻塞直到g2协程执行完毕
执行结果:问题是解决了,但是看起来怪怪的,改变了原有的time,sleep()语法
eat one1
play one1
eat one2
play one2
(5) 终极解决办法 gevent+spawn +monkey.patch_all()
from gevent import monkey;monkey.patch_all()
import time,gevent
def eat():
print("eat one1")
time.sleep(1)
print("eat one2")
def play():
print("play one1")
time.sleep(1)
print("play one2")
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
# 协程的阻塞也是join
g1.join() # 阻塞直到g1协程执行完毕
g2.join() # 阻塞直到g2协程执行完毕
执行结果:perfect 完美!你没有看错,就是这只猴子,老厉害了,解决了所有问题
eat one1
play one1
eat one2
play one2
.