子程序:
或者称为函数,在所有语言中都是层级调用,子程序调用是通过栈实现的。(先进先出)
比如A在执行一部分时调用了B,在B中执行了一般然后又调用C,进入了C,C执行完后,回到了B,B执行完回到A。
协程,英文Coroutines
是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源
协程的调用有点类似与子程序,而又有明显区别。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,再合适的时候返回来接着执行。注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断
它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定
使用yield实现协程:
""" 使用yield实现协程"""
import time
"""使用了yield,所以不是简单的函数了,而是生成器"""
def test_1():
for i in range(5):
print("-----test_1------",i)
yield
time.sleep(0.5)
def test_2():
for i in range(5):
print("-----test_2------",i)
yield
time.sleep(0.5)
def main():
# 创建了两个生成器对象
t1 = test_1()
t2 = test_2()
while True:
# 激活,取test_1执行,遇到yield,停在这个位置。
next(t1)
# 然后在激活 ,在tsst_2执行,遇到了yield,停在这个位置。
next(t2)
# 然后在次循环,又到了t1,回到了上次的地方,又执行到yield.......
if __name__ == "__main__":
main()
输出结果:
greenlet:实现协程
为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单
from greenlet import greenlet
import time
def test1():
while True:
print("---A--")
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print("---B--")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 切换到gr1中运行
gr1.switch()
gevent实现协程:
greenlet已经实现了协程,但是这个还的人工切换,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
演示:如果没有使用monkey......就不能直接使用time.sleep(),需要使用gevent.sleep()
import gevent
import time
from gevent import monkey
monkey.patch_all()
def pri_n(n):
for i in range(n):
print(gevent.getcurrent(), i)
# gevent.sleep(2)
time.sleep(5)
gevent.joinall([
gevent.spawn(pri_n, 5),
gevent.spawn(pri_n, 5),
gevent.spawn(pri_n, 5),
])