一. 协程的概念
协程叫做微线程,它是python中实现多任务的方式之一。它比线程更小,占用更小的执行单元,它自带cpu上下文。
线程操作消耗性能,而协程的切换不那么消耗性能。
二. 简单案例
实现一个简单的协程:
使用yield关键字让函数变成生成器,再使用next关键字对其进行唤醒。
# -*- coding:utf-8 -*-
import time
def work1():
while True:
print("work")
yield
time.sleep(0.5)
def work2():
while True:
print("work2")
yield
time.sleep(0.5)
def main():
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
if __name__ == '__main__':
main()
结果:(循环)
work
work2
work
work2
......
......
三. 使用协程模块 - greenlet
安装:
pip install greenlet
实例:
# -*- coding:utf-8 -*-
import time
from greenlet import greenlet
def work1():
while True:
print("work1")
# work1内切换到work2中去执行
g2.switch()
time.sleep(0.5)
def work2():
while True:
print("work2")
g1.switch()
time.sleep(0.5)
g1 = greenlet(work1)
g2 = greenlet(work2)
# 主程序切换到work1里面去执行
g1.switch()
结果:(循环)
work1
work2
work1
work2
......
......
这种切换占用系统资源很少,它实现多任务可以实现实现上万次操作。
四. 使用协程模块 - gevent
前面的greenlet模块,虽然功能很强大,但是在进行多任务时,实际上还是人工手动切换,那么就要引入一个更强大的模块gevent。
安装:
pip install gevent
原理:
IO的操作一般是比较耗时的,它总会使程序进入等待状态,例如:在进行读写操作时-请输入你的信息,程序就进入一个等待状态。
那么在进行一些耗时的操作时,在这个占用时间内,会自动切换到其他的协程内去执行,等到IO操作、读写操作完成以后,再适当的时间切换到原来的任务中去执行。
用gevent进行协程切换,只要遇到耗时操作需就进行切换,能保证总有协程在执行,而不是一直处于等待状态。
实例:
# -*- coding:utf-8 -*-
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
# 模拟一个耗时操作,注意不是使用time模块中的sleep
gevent.sleep(1)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
结果:
<Greenlet at 0x16367a5a948: f(5)> 0
<Greenlet at 0x16367a5ad48: f(5)> 0
<Greenlet at 0x16367a5ae48: f(5)> 0
<Greenlet at 0x16367a5a948: f(5)> 1
<Greenlet at 0x16367a5ad48: f(5)> 1
<Greenlet at 0x16367a5ae48: f(5)> 1
<Greenlet at 0x16367a5a948: f(5)> 2
<Greenlet at 0x16367a5ad48: f(5)> 2
<Greenlet at 0x16367a5ae48: f(5)> 2
<Greenlet at 0x16367a5a948: f(5)> 3
<Greenlet at 0x16367a5ad48: f(5)> 3
<Greenlet at 0x16367a5ae48: f(5)> 3
<Greenlet at 0x16367a5a948: f(5)> 4
<Greenlet at 0x16367a5ad48: f(5)> 4
<Greenlet at 0x16367a5ae48: f(5)> 4
三个不同协程交替执行
五. 给程序打补丁
猴子补丁,将原程序中的耗时操作代码进行替换,它改变了原代码所有有耗时操作的内容,具有一定风险性。这里将time.sleep打补丁为gevent.sleep
原程序:
# -*- coding:utf-8 -*-
import gevent
import random
import time
def work(name):
for i in range(10):
print(name, i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(work, 'work1'),
gevent.spawn(work, 'work2')
])
原结果:
work1 0
work1 1
work1 2
work1 3
work1 4
work1 5
work1 6
work1 7
work1 8
work1 9
work2 0
work2 1
work2 2
work2 3
work2 4
work2 5
work2 6
work2 7
work2 8
work2 9
协程之间按顺序执行,不符合我们的预期结果,比较耗费资源。
打补丁后:
# -*- coding:utf-8 -*-
import gevent
import random
import time
# 加上猴子补丁
from gevent import monkey
# 有耗时操作时,替换原程序中的耗时代码
# 猴子补丁,将自己导入的模块进行替换,且改变代码块内容
# 这里改变了time模块,将time.sleep变成gevent.sleep
monkey.patch_all()
def work(name):
for i in range(10):
print(name, i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(work, 'work1'),
gevent.spawn(work, 'work2')
])
结果:
work1 0
work2 0
work1 1
work2 1
work2 2
work1 2
work2 3
work2 4
work1 3
work2 5
work2 6
work2 7
work1 4
work1 5
work2 8
work1 6
work1 7
work2 9
work1 8
work1 9
实现自动切换,多个协程之间交替执行,一个协程阻塞就开启另一个协程执行。
来源:CSDN
作者:wisewho?
链接:https://blog.csdn.net/weixin_44225602/article/details/103744837