python 异步编程:协程

打印 上一主题 下一主题

主题 1045|帖子 1045|积分 3135

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
什么是协程?

协程(Coroutine)是一种用于并发编程的技术,它允许在单个线程内实现多任务调度。协程可以在执行过程中暂停,并在需要时恢复执行,这使得它们非常适合处置处罚I/O密集型任务,如网络哀求、文件读写等。

  • 非抢占式调度:协程由程序显式地控制切换点,不像线程那样由操作系统调度。这意味着你可以精确控制何时暂停和恢复一个协程。
  • 轻量级:由于不需要创建新的线程或历程,协程的开销更小。
  • 单线程内并发:所有的任务都运行在一个单独的主线程中,因此制止了多线程编程中的一些复杂性,如竞争条件和死锁。
协程的实现

在 Python 中,协程的实现有多种,此中主要依赖于生成器和 asyncio 库。关于asyncio的详细用法本章仅做实现示例,下章再详细拆解。
greenlet实现

  1. pip3 install greenlet
复制代码
  1. from greenlet import greenlet
  2. def func1():
  3.     print(1)
  4.     gr2.switch()
  5.     print(2)
  6.     gr2.switch()
  7. def func2():
  8.     print(3)
  9.     gr1.switch()
  10.     print(4)
  11. gr1 = greenlet(func1)
  12. gr2 = greenlet(func2)
  13. gr1.switch()
复制代码
结果:
  1. 1
  2. 3
  3. 2
  4. 4
复制代码


  • 定义两个函数 func1() 和 func2(),每个函数内部都有多个步骤,并且在某些步骤之后会调用对方的 switch 方法来进行上下文切换。
  • 利用 greenlet() 函数分别将这两个函数包装成 Greenlets:gr1 和 gr2.
  • 最后,通过调用 gr1.switch() 开始执行第一个任务,这会导致程序在差异任务之间来回切换。
生成器实现

什么是yield关键字?

yield 关键字用于定义一个生成器(generator)函数。生成器是一种特别的迭代器,它允许你逐个产生值,而不是一次性创建并返回一个包含所有值的列表。利用 yield 的函数在执行过程中可以多次 yield 值,每次 yield 都会返回一个值,并在下一次从该点继续执行。
yield 是 Python 中实现协程和异步编程的基础,特别是在 Python 3.3 之前,当时的 asyncio 库依赖于生成器来实现协程。在 Python 3.5 及以后版本中,引入了 async 和 await 语法,提供了更高级的异步编程本领,但 yield 仍然是 Python 编程中一个非常有用的关键字。

  • 惰性盘算:生成器在调用时不会立即盘算所有值,而是在每次迭代时盘算并产生下一个值。
  • 状态保存:当生成器函数 yield 一个值时,它的所有当地变量和当前的执行状态都会被保存,以便下次从该点继续执行。
  • 逐个产生:生成器一次产生一个值,这使得它们在处置处罚大量数据时非常高效,因为不需要一次性将所有数据加载到内存中。
  • 可迭代对象:生成器本身是可迭代对象,可以被 for 循环或 next() 函数迭代。
  1. def count_up_to(num):
  2.     """
  3.     `count_up_to` 函数是一个生成器,它在每次迭代时 `yield` 当前的 `count` 值,并在下一次迭代时从上次停下的地方继续执行。
  4.     不使用yield的话,使用这段代码可以达到同样的效果:
  5.     result = list()
  6.     count = 1
  7.     while count <= num:
  8.         result.append(count)
  9.         count += 1
  10.     return result
  11.     """
  12.     count = 1
  13.     while count <= num:
  14.         yield count
  15.         count += 1
  16. # 使用生成器
  17. for number in count_up_to(5):
  18.     print(number)
  19. print("分割线".center(30, "="))
  20. def chain_generators(*gens):
  21.     """
  22.     yield from 允许你将一个生成器的输出委托给另一个生成器,简化了生成器的嵌套调用。
  23.     如果不使用yield from来简化:
  24.     for gen in gens:
  25.         # 迭代每个生成器对象
  26.         for value in gen:
  27.             # 逐个 yield 生成器的值
  28.             yield value
  29.     """
  30.     for gen in gens:
  31.         yield from gen
  32. for number in chain_generators(count_up_to(2), count_up_to(3)):
  33.     print(number)
复制代码
结果输出:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. =============分割线==============
  7. 1
  8. 2
  9. 1
  10. 2
  11. 3
复制代码
实现协程示例

  1. def func1():
  2.     yield 1
  3.     yield from func2()
  4.     yield 2
  5. def func2():
  6.     yield 3
  7.     yield 4
  8. f1 = func1()
  9. for item in f1:
  10.     print(item)
复制代码
结果
  1. 1
  2. 3
  3. 4
  4. 2
复制代码


  • 首先,f1 开始执行,yield 1 被执行,生成器产生值 1。
  • 然后,yield from func2() 被执行。func2 产生第一个值 3,f1 将其通报给迭代器,以是 3 被打印出来。
  • func2 继续产生第二个值 4,f1 同样将其通报给迭代器,以是 4 也被打印出来。
  • func2 没有更多的 yield 语句,以是 yield from 表达式完成,控制权回到 f1。
  • f1 继续执行,yield 2 被执行,生成器产生值 2。
asyncio实现

旧版本实现

asyncio 是 Python 的一个标准库,用于编写单线程并发代码。
在python3.4及之后,asyncio 库可以利用 @asyncio.coroutine 装饰器来定义协程。这个装饰器是编写协程的早期方法,它允许你在函数定义之前添加 @asyncio.coroutine,以便利用 yield from 来暂停和恢复协程的执行。
  1. import asyncio
  2. # 在3.8版本后这种装饰器的写法就废弃了 ,让你用async def来定义函数替代。运行会提示警告:
  3. # DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
  4. @asyncio.coroutine
  5. def func1():
  6.     print(1)
  7.     # 模拟网络IO请求
  8.     yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
  9.     print(2)
  10. @asyncio.coroutine
  11. def func2():
  12.     print(3)
  13.     # 模拟网络IO请求
  14.     yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
  15.     print(4)
  16. tasks = [
  17.     asyncio.ensure_future(func1()),
  18.     asyncio.ensure_future(func2())
  19. ]
  20. loop = asyncio.get_event_loop()
  21. loop.run_until_complete(asyncio.wait(tasks))
复制代码
输出:
  1. 1
  2. 3
  3. 2
  4. 4
复制代码

  • 首先,它将运行 func1,直到遇到 asyncio.sleep(2)。
  • 由于 asyncio.sleep(2) 是一个异步等待调用,事件循环将挂起 func1 并查抄是否有其他任务可以运行。
  • 然后,事件循环切换到 func2 并执行它,直到遇到 asyncio.sleep(2),同样被挂起。
  • 事件循环再次查抄任务队列,发现没有其他任务可以运行,将等待一段时间(由 asyncio.sleep 的参数决定)。
  • 2 秒后,func1 和 func2 中的 asyncio.sleep 都完成,事件循环将恢复它们的执行。
新版本实现

在3.7版本后,保举利用 async def 定义协程,并用 await 来等待异步操作。
  1. import asyncio
  2. async def func1():
  3.     """
  4.     相比上面的使用装饰器,推荐新的写法:
  5.         将装饰器@asyncio.coroutine去掉,def方法加上async前缀
  6.         yield from 改为使用await
  7.     """
  8.     print(1)
  9.     # 模拟网络IO请求
  10.     await asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
  11.     print(2)
  12. async def func2():
  13.     print(3)
  14.     # 模拟网络IO请求
  15.     await asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
  16.     print(4)
  17. tasks = [
  18.     asyncio.ensure_future(func1()),
  19.     asyncio.ensure_future(func2())
  20. ]
  21. loop = asyncio.get_event_loop()
  22. loop.run_until_complete(asyncio.wait(tasks))
复制代码
协程与多线程、多历程的区别接洽

协程(Coroutines)

协程是一种程序组件,能够在等待操作完成时挂起执行,而无需壅闭整个线程。它们通常用于 I/O 密集型任务,因为 I/O 操作(如网络哀求、文件读写)通常涉及等待。协程的优点包括:


  • 轻量级:协程的创建和切换开销远小于线程。
  • 非抢占式:协程的执行次序由程序逻辑控制,而不是由操作系统强制抢占。
  • 易于编写:利用 async/await 语法可以使异步代码的逻辑更清晰。
Python 中的 asyncio 库提供了对协程的支持。
多线程(Multithreading)

多线程允许在同一时间内在同一个历程中并行运行多个线程。线程共享历程的内存和资源,这使得线程间通信和数据共享变得容易,但也引入了同步和竞态条件的问题。多线程的优点包括:


  • 资源共享:线程可以共享历程的内存和文件形貌符等资源。
  • 简化编程:线程的创建和管理相对简单。
  • 适用性:适合盘算密集型和 I/O 密集型任务。
Python 的 threading 模块提供了多线程的支持。
多历程(Multiprocessing)

多历程是指在操作系统级别上同时运行多个历程。每个历程有自己的内存空间,这意味着历程间通信需要通过明确的 IPC(历程间通信)机制。多历程的优点包括:


  • 隔离性:历程间相互独立,一个历程的瓦解不会直接影响到其他历程。
  • 资源分配:操作系统可以有效地管理差异历程的资源分配。
  • CPU 密集型任务:由于每个历程有自己的内存空间,多历程适合处置处罚 CPU 密集型任务。
Python 的 multiprocessing 模块提供了多历程的支持。
区别与接洽



  • 资源占用:协程是单线程内的并发,资源占用最少;多线程共享历程资源,资源占用适中;多历程有最大的资源占用。
  • 上下文切换开销:协程上下文切换的开销最小;多线程次之;多历程的上下文切换开销最大。
  • 编程复杂度:协程编程模型最简单;多线程需要处置处罚线程安全和同步问题;多历程由于历程隔断离,编程复杂度介于协程和多线程之间。
  • 适用场景:协程适合 I/O 密集型任务;多线程适合盘算和 I/O 混合型任务;多历程适合 CPU 密集型任务和需要高隔离性的场景。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表