文章目录
- 协程Coroutines
- 协程的核心(控制流的让出和恢复)
- 协程和多线程比较
- 协程的优点
- 协程的缺点
- asyncio实现协程(重点)
协程Coroutines
协程,全称是“协同程序”,用来实现任务协作。是一种在线程中,比线程更加轻量级的存在,由程序员自己写程序来管理。
当出现IO阻塞时,CPU一直等待IO返回,处于空转状态。这时候用协程,可以执行其他任务。当IO返回结果后,再回来处理数据。充分利用了IO等待的时间,提高了效率。
协程的核心(控制流的让出和恢复)
1每个协程有自己的执行栈,可以保存自己的执行现场
2 可以由用户程序按需创建协程(比如:遇到io操作),协程“主动让出(yield)”执行权时候,会保存执行现场(保存中断时的寄存器上下文和栈),然后切换到其他协程
3 协程恢复执行(resume)时,根据之前保存的执行现场恢复到中断前的状态,继续执行,这样就通过协程实现了轻量的由用户态调度的多任务模型
协程和多线程比较
比如,有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。
1 在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。
2 多线程版本中,这3个任务分别在独立的线程中执行。这些线程由操作系统来管理,在多处理器系统上可以并行处理,或者在单处理器系统上交错执行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。
3 协程版本的程序中,3个任务交错执行,但仍然在一个单独的线程控制中。当处理I/O或者其他昂贵的操作时,注册一个回调到事件循环中,然后当I/O操作完成时继续执行。回调描述了该如何处理某个事件。事件循环轮询所有的事件,当事件到来时将它们分配给等待处理事件的回调函数。
协程的优点
1 由于自身带有上下文和栈,无需线程上下文切换的开销,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级;
2 无需原子操作的锁定及同步的开销;
3 方便切换控制流,简化编程模型
4 单线程内就可以实现并发的效果,最大限度地利用cpu,且可扩展性高,成本低(注:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理)
asyncio
协程是写爬虫比较好的方式。比多线程和多进程都好开辟新的线程和进程是非常耗时的
协程的缺点
1 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上。
2 当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
asyncio实现协程(重点)
1 正常的函数执行时是不会中断的,所以你要写一个能够中断的函数,就需要加 asyncasync
用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(假设挂起条件是sleep(5)
)消失后,也就是5秒到了再回来执行
2 await
用来用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂
起,去执行其他的异步程序。
3 asyncio
是python3.5之后的协程模块,是python实现并发重要的包,这个包使用事件循环驱动实现并发。
不使用协程,耗时6秒
#coding=utf-8
import time
def func1():
for i in range(3):
print(f'北京:第{i}次打印啦')
time.sleep(1)
return "func1执行完毕"
def func2():
for k in range(3):
print(f'上海:第{k}次打印了')
time.sleep(1)
return "func2执行完毕"
def main():
func1()
func2()
if __name__ == '__main__':
start_time = time.time()
main()
end_time = time.time()
print(f"耗时{end_time-start_time}") #不使用协程,耗时6秒
使用协程,耗时3秒
#coding=utf-8
import asyncio
import time
async def func1(): #async表示方法是异步的
for i in range(3):
print(f'北京:第{i}次打印啦')
await asyncio.sleep(1)
return "func1执行完毕"
async def func2():
for k in range(3):
print(f'上海:第{k}次打印了' )
await asyncio.sleep(1)
return "func2执行完毕"
async def main():
res = await asyncio.gather(func1(),func2())
#await异步执行func1方法
#返回值为函数的返回值列表,本例为["func1执行完毕", "func2执行完毕"]
print(res)
if __name__ == '__main__':
start_time = time.time()
asyncio.run(main())
end_time = time.time()
print(f"耗时{end_time-start_time}") #耗时3秒,效率极大提高