VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > python教程 >
  • python基础教程之Python3标准库:asyncio异步I/O、事件循环和并发工具

本站最新发布   Python从入门到精通|Python基础教程
试听地址  
https://www.xin3721.com/eschool/pythonxin3721/


作者:@小灰灰
本文为作者原创,转载请注明出处:https://www.cnblogs.com/liuhui0308/p/12600076.html


1. asyncio异步I/O、事件循环和并发工具

asyncio模块提供了使用协程构建并发应用的工具。threading模块通过应用线程实现并发,multiprocessing使用系统进程实现并发,asyncio则使用一种单线程单进程方法来实现并发,应用的各个部分会彼此合作,在最优的时刻显式地切换任务。大多数情况下,会在程序阻塞等待读写数据时发生这种上下文切换,不过asyncio也支持调度代码在将来的某个特定时间运行,从而支持一个协程等待另一个协程完成,以处理系统信号和识别其他一些事件(这些事件可能导致应用改变其工作内容)。

1.1 异步并发概念

使用其他并发模型的大多数程序都采用线性方式编写,而且依赖于语言运行时系统或操作系统的底层线程或进程管理来适当地改变上下文。基于asyncio的应用要求应用代码显式地处理上下文切换,要正确地使用相关技术,这取决于是否能正确理解一些相关联的概念。asyncio提供的框架以一个事件循环(event loop)为中心,这是一个首类对象,负责高效地处理I/O事件、系统事件和应用上下文切换。目前已经提供了多个循环实现来高效地利用操作系统的功能。尽管通常会自动地选择一个合理的默认实现,但也完全可以在应用中选择某个特定的事件循环实现。在很多情况下这会很有用,例如,在Windows下,一些循环类增加了对外部进程的支持,这可能会以牺牲一些网络I/O效率为代价。与事件循环交互的应用要显式地注册将运行的代码,让事件循环在资源可用时向应用代码发出必要的调用。例如,一个网络服务器打开套接字,然后注册为当这些套接字上出现输入事件时服务器要得到通知。事件循环在建立一个新的进入连接或者在数据可读取时都会提醒服务器代码。当前上下文中没有更多工作可做时,应用代码要再次短时间地交出控制。例如,如果一个套接字再没有更多的数据可以读取,那么服务器会把控制交回给事件循环。
将控制交还给事件循环的机制依赖于Python的协程(coroutine),这是一些特殊的函数,可以将控制交回给调用者而不丢失其状态。协程与生成器函数非常类似;实际上,在Python3.5版本之前对协程未提供原生支持时,可以用生成器来实现协程。asyncio还为协议(protocol)和传输(transport)提供了一个基于类的抽象层,可以使用回调编写代码而不是直接编写协程。在基于类的模型和协程模型中,可以通过重新进入事件循环显式地改

变上下文,以取代Python多线程实现中隐式的上下文改变。future是一个数据结构,表示还未完成的工作结果。事件循环可以监视Future对象是否完成,从而允许应用的一部分等待另一部分完成一些工作。除了future,asyncio还包括其他并发原语,如锁和信号量。
Task是Future的一个子类,它知道如何包装和管理一个协程的执行。任务所需的资源可用时,事件循环会调度任务运行,并生成一个结果,从而可以由其他协程消费。

1.2 利用协程合作完成多任务

协程是一个专门设计用来实现并发操作的语言构造。调用协程函数时会创建一个协程对象,然后调用者使用协程的send()方法运行这个函数的代码。协程可以使用await关键字(并提供另一个协程)暂停执行。暂停时,这个协程的状态会保留,使得下一次被唤醒时可以从暂停的地方恢复执行。

1.2.1 启动一个协程

asyncio事件循环可以采用多种不同的方法启动一个协程。最简单的方法是使用run_until complete(),并把协程直接传人这个方法。


  1. import asyncio
  2.  
  3. async def coroutine():
  4. print('in coroutine')
  5.  
  6. event_loop = asyncio.get_event_loop()
  7. try:
  8. print('starting coroutine')
  9. coro = coroutine()
  10. print('entering event loop')
  11. event_loop.run_until_complete(coro)
  12. finally:
  13. print('closing event loop')
  14. event_loop.close()

第一步是得到事件循环的一个引用。可以使用默认的循环类型,也可以实例化一个特定的循环类。在这个例子中使用了默认循环。run_until_complete()方法用这个协程启动循环;协程返回退出时这个方法会停止循环。

1.2.2 从协程返回值 

协程的返回值传回给启动并等待这个协程的代码。


  1. import asyncio
  2.  
  3. async def coroutine():
  4. print('in coroutine')
  5. return 'result'
  6.  
  7. event_loop = asyncio.get_event_loop()
  8. try:
  9. return_value = event_loop.run_until_complete(
  10. coroutine()
  11. )
  12. print('it returned: {!r}'.format(return_value))
  13. finally:
  14. event_loop.close()

在这里,run_unitil_complete()还会返回它等待的协程的结果。

1.2.3 串链协程

一个协程可以启动另一个协程并等待结果,从而可以更容易地将一个任务分解为可重用的部分。下面的例子有两个阶段,它们必须按顺序执行,不过可以与其他操作并发运行。


  1. import asyncio
  2.  
  3. async def outer():
  4. print('in outer')
  5. print('waiting for result1')
  6. result1 = await phase1()
  7. print('waiting for result2')
  8. result2 = await phase2(result1)
  9. return (result1, result2)
  10.  
  11. async def phase1():
  12. print('in phase1')
  13. return 'result1'
  14.  
  15. async def phase2(arg):
  16. print('in phase2')
  17. return 'result2 derived from {}'.format(arg)
  18.  
  19. event_loop = asyncio.get_event_loop()
  20. try:
  21. return_value = event_loop.run_until_complete(outer())
  22. print('return value: {!r}'.format(return_value))
  23. finally:
  24. event_loop.close()

这里使用了await关键字而不是向循环增加新的协程。因为控制流已经在循环管理的一个协程中,所以没有必要告诉循环管理这些新协程。

1.2.4 生成器代替协程 

协程函数是asyncio设计中的关键部分。它们提供了一个语言构造,可以停止程序某一部分的执行,保留这个调用的状态,并在以后重新进人这个状态。所有这些动作都是并发框架很重要的功能。 Python3.5引入了一些新的语言特性,可以使用async def以原生方式定义这些协程,以及使用await交出控制,asyncio的例子利用了这些新特性。Python3的早期版本可以使用由 asyncio.coroutine()修饰符包装的生成器函数和yield from来达到同样的效果。


  1. import asyncio
  2.  
  3. @asyncio.coroutine
  4. def outer():
  5. print('in outer')
  6. print('waiting for result1')
  7. result1 = yield from phase1()
  8. print('waiting for result2')
  9. result2 = yield from phase2(result1)
  10. return (result1, result2)
  11.  
  12. @asyncio.coroutine
  13. def phase1():
  14. print('in phase1')
  15. return 'result1'
  16.  
  17. @asyncio.coroutine
  18. def phase2(arg):
  19. print('in phase2')
  20. return 'result2 derived from {}'.format(arg)
  21.  
  22. event_loop = asyncio.get_event_loop()
  23. try:
  24. return_value = event_loop.run_until_complete(outer())
  25. print('return value: {!r}'.format(return_value))
  26. finally:
  27. event_loop
相关教程