前言:
什么是协程?协又叫又称微线程,英文名Coroutine,协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适gr的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
通俗的描述:
-
协程看做是是线程中的一个特殊的函数,这个函数执行的时候,可以在某个地方暂停,并且可以重新在暂停处,继续运行,协程在进行切换的时候,只需要保存当前协程函数中的一些临时变量等信息,然后切换到另外一个函数中执行,并且切换的次数以及什么时候再切换到原来的函数,都可以由开发者自己决定
-
协成切换的时候,既不涉及到资源切换,也不涉及到操作系统的调度,而是在在同一个程序中切换不同的函数执行,所以协成占用的资源非常少,切换得时候几乎不耗费什么资源。
-
协程与进程、线程相比并不是一个维度的概念
-
协程有分为原生协程和第三方库(gevent模块)实现的协程两种。
一、协程的第三方库:gevent模块:
官方文档:https://www.gevent.org/api/gevent.html
-
什么是gevent
gevent 其原理是当一个协程遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
-
协程正常的情况下:遇到异步耗时(IO操作)才会进行切换
I: input 输入(文件读取,网络请求数据接收)
o: output 输出(往文件中写内容,发送网络请求) -
gevent模式的具体使用
第一步:在程序中如果希望遇到IO和耗时就进行切换协程中执行,则需要添加补丁
打补丁的作用是:将普通的耗时和IO操作—>转换为异步耗时和IO操作from gevent import monkey # 对所有的IO和耗时操作打补丁 monkey.patch_all()
第二步:创建并启动协程:gevent.spawn(任务函数)
# 1. 创建协程对象 g1 = gevent.spawn(func1) # 等待协程g1执行结束 g2 = gevent.spawn(func2) # 等待协程g2执行结束
第三步:等待协程执行结束
-
第一种: join()方法
# 2、单个协程对象执行结束 g1.join() g2.join()
-
第二种: gevent.joinall([a,b])
# 3、多个协程对象时等待协程执行结束 gevent.joinall([g1,g2])
-
-
实现没有异步耗时(IO操作)时协程是不会执行的
# 函数 1 def func1(): for i in range(3): print("=========== 正在做事情1 ============") time.sleep(1) # time模块的等待并不会执行 # 函数 2 def func2(): for i in range(4): print("=========== 正在做事情2 ============") time.sleep(1) # 1. 创建协程对象 g1 = gevent.spawn(func1) # 等待协程g1执行结束 g2 = gevent.spawn(func2) # 等待协程g2执行结束 # # 2、等待协程执行结束 # g1.join() # g2.join() # 3、多个协程对象时等待协程执行结束 gevent.joinall([g1,g2]) print("========================== 执行结束 ==========================")
执行结果:
- 在上述代码中,去创建协程对象,等待执行结束后,发现两个任务还是一个一个的去执行,并没有同时去执行,这是因为协程只有遇到IO耗时等待的时候才会去执行,而代码中并没有涉及到IO耗时等待的操作,time模块的等待并不会触发。
- 在上述代码中,去创建协程对象,等待执行结束后,发现两个任务还是一个一个的去执行,并没有同时去执行,这是因为协程只有遇到IO耗时等待的时候才会去执行,而代码中并没有涉及到IO耗时等待的操作,time模块的等待并不会触发。
-
实现有异步耗时(IO操作)时协程的执行情况
-
第一种方式:通常情况下如果要协程去执行,就需要代码里涉及到IO等耗时操作,gevent模快中也有一个sleep方法:
# 函数1 def func1(): for i in range(3): print("=========== 正在做事情1 ============") gevent.sleep(1) # 这是异步耗时等待 # 函数2 def func2(): for i in range(4): print("=========== 正在做事情2 ============") gevent.sleep(1) # 这是异步耗时等待 # 1. 创建协程对象 g1 = gevent.spawn(func1) # 等待协程g1执行结束 g2 = gevent.spawn(func2) # 等待协程g2执行结束 # 2、多个协程对象时等待协程执行结束 gevent.joinall([g1,g2]) print("========================== 执行结束 ==========================")
执行结果:
- 在上述代码中,去创建协程对象,等待执行结束后,发现两个任务是并行去执行是,代码中有涉及到IO耗时等待的操作,gevent.sleep(1), 协程遇到IO耗时等待的时候就会去执行。
- 在上述代码中,去创建协程对象,等待执行结束后,发现两个任务是并行去执行是,代码中有涉及到IO耗时等待的操作,gevent.sleep(1), 协程遇到IO耗时等待的时候就会去执行。
-
第二种方式:通常情况下如果要协程去执行,就需要代码里涉及到IO等耗时操作,也可以通过打补丁的方式去执行:
from gevent import monkey # 对所有的IO和耗时操作打补丁 monkey.patch_all() # 函数1 def func1(): for i in range(3): print("=========== 正在做事情1 ============") time.sleep(1) # 函数2 def func2(): for i in range(4): print("=========== 正在做事情2 ============") time.sleep(1) # 1. 创建协程对象 g1 = gevent.spawn(func1) # 等待协程g1执行结束 g2 = gevent.spawn(func2) # 等待协程g2执行结束 # 2、多个协程对象时等待协程执行结束 gevent.joinall([g1,g2]) print("========================== 执行结束 ==========================")
执行结果:
- 在上述代码中,首先去进行了打补丁的操作,在实现等待的方式上,使用了time模块,去创建协程对象,等待执行结束后,发现两个任务是并行去执行是,这是因为monkey在进行补丁操作时,自动将普通的耗时和IO操作—>转换为异步耗时和IO操作去执行,所以看到两个任务是并行去执行是。
- 在上述代码中,首先去进行了打补丁的操作,在实现等待的方式上,使用了time模块,去创建协程对象,等待执行结束后,发现两个任务是并行去执行是,这是因为monkey在进行补丁操作时,自动将普通的耗时和IO操作—>转换为异步耗时和IO操作去执行,所以看到两个任务是并行去执行是。
-