Python 中的协程 (4) asyncio模块

asyncio的核心概念与基本架构

  本文针对的是python3.4以后的版本的,因为从3.4开始才引入asyncio,后面的3.5 3.6 3.7版本是向前兼容的,只不过语法上面有稍微的改变。比如在3.4版本中使用@asyncio.coroutine装饰器和yield from语句,但是在3.5以后的版本中使用async、await两个关键字代替,虽然语法上稍微有所差异,但是原理是一样的。

 

1 asyncio 组成的基本概念

1 协程函数的作用

(1)result = yield from future,返回future的结果。

(2)result = yield from coroutine,等候另一个协程函数返回结果或者是触发异常

(3)result= yield from task,返回一个task的结果

(4)return expression,作为一个函数抛出返回值

(5)raise exception

2 事件循环 event_loop

如何理解事件循环:

线程一直在各个协程方法之间永不停歇的游走,遇到一个yield from 或者await就悬挂起来,然后又走到另外一个方法,依次进行下去,知道事件循环所有的方法执行完毕。实际上loop是BaseEventLoop的一个实例,我们可以查看定义,它到底有哪些方法可调用

协程函数,不是像普通函数那样直接调用运行的,必须添加到事件循环中,然后由事件循环去运行,单独运行协程函数是不会有结果的。

import time
import asyncio
async def say_after_time(delay,what):
        await asyncio.sleep(delay)
        print(what)

async def main():
        print(f"开始时间为: {time.time()}")
        await say_after_time(1,"hello")
        await say_after_time(2,"world")
        print(f"结束时间为: {time.time()}")
        
''' 直接运行 '''        
# >>> main()
# <coroutine object main at 0x1053bb7c8>       

'''  需要通过事件循环来调用'''
loop=asyncio.get_event_loop()    #创建事件循环对象
#loop=asyncio.new_event_loop()   #与上面等价,创建新的事件循环
loop.run_until_complete(main())  #通过事件循环对象运行协程函数
loop.close()

(1)获取事件循环对象的几种方式:

  1. loop=asyncio.get_running_loop(),返回(获取)在当前线程中正在运行的事件循环,如果没有正在运行的事件循环,则会显示错误

  2. loop=asyncio.get_event_loop() ,获得一个事件循环,如果当前线程还没有事件循环,则创建一个新的事件循环loop

  3. loop=asyncio.set_event_loop(loop), 设置一个事件循环为当前线程的事件循环;

  4. loop=asyncio.new_event_loop() ,创建一个新的事件循环

(2)通过事件循环运行协程函数的两种方式:

  1. 创建事件循环对象loop,即 asyncio.get_event_loop(),通过事件循环运行协程函数

  2. 直接通过 asyncio.run(function_name) 运行协程函数。

  但是需要注意的是,首先run函数是python3.7版本新添加的,前面的版本是没有的;其次,这个run函数总是会创建一个新的事件循环并在run结束之后关闭事件循环,所以,如果在同一个线程中已经有了一个事件循环,则不能再使用这个函数了,因为同一个线程不能有两个事件循环,而且这个run函数不能同时运行两次,因为他已经创建一个了。即同一个线程中是不允许有多个事件循环loop的。 asyncio.run()是python3.7 新添加的内容,也是后面推荐的运行任务的方式,因为它是高层API,后面会讲到它与asyncio.run_until_complete()的差异性,run_until_complete()是相对较低层的API。

3 什么是awaitable对象

有三类对象是可等待的,即 coroutines , Tasks , and Futures .

coroutine :本质上就是一个函数,一前面的生成器yield和yield from为基础,不再赘述;

Tasks : 任务,顾名思义,就是要完成某件事情,其实就是对协程函数进一步的封装;

Future :它是一个“更底层”的概念,他代表一个异步操作的最终结果,因为异步操作一般用于耗时操作,结果不会立即得到,会在“将来”得到异步运行的结果,故而命名为 Future。

三者的关系,coroutine 可以自动封装成 task ,而Task是 Future 的子类。

4 什么是task任务

Task用来 并发调度的协程, 单纯的协程函数仅仅是一个函数而已,将其包装成任务,任务是可以包含各种状态的,异步编程最重要的就是对异步操作状态的把控了。

(1)创建任务(两种方法):

方法一:task = asyncio.create_task(coro()) # 这是3.7版本新添加的

方法二:task = asyncio.ensure_future(coro()) ,也可以使用loop.create_future()loop.create_task(coro) 也是可以的。

(2)获取某一个任务的方法:

方法一:task=asyncio.current_task(loop=None);返回在某一个指定的loop中,当前正在运行的任务,如果没有任务正在运行,则返回None;如果loop为None,则默认为在当前的事件循环中获取,

方法二:asyncio.all_tasks(loop=None);返回某一个loop中还没有结束的任务;

5 什么是future?

  Future是一个较低层的可等待(awaitable)对象,他表示的是异步操作的最终结果,当一个Future对象被等待的时候,协程会一直等待,直到Future已经运算完毕。 Future是Task的父类,一般情况下,已不用去管它们两者的详细区别,也没有必要去用Future,用Task就可以了,返回 future 对象的低级函数的一个很好的例子是 loop.run_in_executor().

  

2 asyncio的基本架构

asyncio分为高层API和低层API。我们前面所讲的Coroutine和Tasks属于高层API,而Event Loop 和Future属于低层API。所谓的高层API主要是指那些asyncio.xxx()的方法。

High-level APIs

●Coroutines and Tasks(本文要写的) ​ ●Streams ​ ●Synchronization Primitives ​ ●Subprocesses ​ ●Queues ​ ●Exceptions

Low-level APIs

●Event Loop(下一篇要写的) ​ ●Futures ​ ●Transports and Protocols ​ ●Policies ​ ●Platform Support

1 常见的一些高层API方法

猜你喜欢

转载自www.cnblogs.com/wang-kai-1994/p/10375258.html