协程

协程Coroutines

协程是什么

image-20211204102521781

协程,Coroutines,也叫作纤程(Fiber)

协程,全称是“协同程序”,用来实现任务协作。是一种在线程中,比线程更加轻量级的存在,由程序员自己写程序来管理。

当出现IO阻塞时,CPU一直等待IO返回,处于空转状态。这时候用协程,可以执行其他任务。当IO返回结果后,再回来处理数据。充分利用了IO等待的时间,提高了效率。

一个故事说明进程、线程、协程的关系

image-20211204105438180

乔布斯想开工厂生产手机,费劲力气,制作一条生产线,这个生产线上有很多的器件以及材料。一条生产线就是一个进程。

只有生产线是不够的,所以找五个工人来进行生产,这个工人能够利用这些材料最终一步步的将手机做出来,这五个工人就是五个线程。

为了提高生产率,想到3种办法:

  1. 一条生产线上多招些工人,一起来做手机,这样效率是成倍増长,即单进程多线程方式
  2. 多条生产线,每个生产线上多个工人,即多进程多线程
  3. 乔布斯深入一线发现工人不是那么忙,有很多等待时间。于是规定:如果某个员工在等待生产线某个零件生产时 ,不要闲着,干点其他工作。也就是说:如果一个线程等待某些条件,可以充分利用这个时间去做其它事情,这就是:协程方式。

协程的核心(控制流的让出和恢复)

  1. 每个协程有自己的执行栈,可以保存自己的执行现场
  2. 可以由用户程序按需创建协程(比如:遇到io操作)
  3. 协程“主动让出(yield)”执行权时候,会保存执行现场(保存中断时的寄存器上下文和栈),然后切换到其他协程
  4. 协程恢复执行(resume)时,根据之前保存的执行现场恢复到中断前的状态,继续执行,这样就通过协程实现了轻量的由用户态调度的多任务模型

协程和多线程比较

比如,有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。阻塞在I/O操作上所花费的时间已经用灰色框标示出来了。

image-20211205151706869

  1. 在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。
  2. 多线程版本中,这3个任务分别在独立的线程中执行。这些线程由操作系统来管理,在多处理器系统上可以并行处理,或者在单处理器系统上交错执行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。
  3. 协程版本的程序中,3个任务交错执行,但仍然在一个单独的线程控制中。当处理I/O或者其他昂贵的操作时,注册一个回调到事件循环中,然后当I/O操作完成时继续执行。回调描述了该如何处理某个事件。事件循环轮询所有的事件,当事件到来时将它们分配给等待处理事件的回调函数。

协程的优点

  1. 由于自身带有上下文和栈,无需线程上下文切换的开销,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级;
  2. 无需原子操作的锁定及同步的开销;
  3. 方便切换控制流,简化编程模型
  4. 单线程内就可以实现并发的效果,最大限度地利用cpu,且可扩展性高,成本低(注:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理)

asyncio协程是写爬虫比较好的方式。比多线程和多进程都好. 开辟新的线程和进程是非常耗时的。

协程的缺点

  1. 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上。
  2. 当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

使用yield实现协程(已淘汰,了解即可)

Python中的协程经历了很长的一段发展历程。其大概经历了如下三个阶段:

  1. 最初的生成器变形yield/send
  2. 引入@asyncio.coroutineyield from
  3. Python3.5版本后,引入async/await关键字

【示例】不使用协程执行多个任务

#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秒
​
'''
执行结果:
北京:第0次打印啦
北京:第1次打印啦
北京:第2次打印啦
上海:第0次打印了
上海:第1次打印了
上海:第2次打印了
耗时6.042387008666992
'''

 【示例】使用yield协程,实现任务切换

#coding=utf-8
import time

def func1():
  for i in range(3):
    print(f'北京:第{i}次打印啦')
    yield       # 只要方法包含了yield,就变成一个生成器, 开始执行func2
    time.sleep(1)

def func2():
  g = func1()   # func1是一个生成器,func1()就不会直接调用,需要通过next()或for循环调用
  print(type(g))
  for k in range(3):
    print(f'上海:第{k}次打印了' )
    next(g)     # 转换执行func1的代码
    time.sleep(1)

#有了yield,我们实现了两个任务的切换+保存状态
if __name__ == '__main__':
  start_time = time.time()
  func2()
  end_time = time.time()
  print(f"耗时{end_time-start_time}")  #耗时5.0秒,效率差别不大

'''
执行结果:

上海:第0次打印了
北京:第0次打印啦
上海:第1次打印了
北京:第1次打印啦
上海:第2次打印了
北京:第2次打印啦
耗时5.039638996124268
'''

asyncio实现协程(重点)

  1. 正常的函数执行时是不会中断的,所以你要写一个能够中断的函数,就需要加async
  2. async 用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(假设挂起条件是sleep(5))消失后,也就是5秒到了再回来执行
  3. await 用来用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂起,去执行其他的异步程序。
  4. asyncio是python3.5之后的协程模块,是python实现并发重要的包,这个包使用事件循环驱动实现并发。

使用asyncio,整体执行完,耗时3秒,效率极大提高。

【示例】asyncio异步IO的典型使用方式

#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秒,效率极大提高

 

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值