python异步方法asyncio的使用小结

感觉上面写的太分散了,这里总结下,参考了大佬的文章不做别的用途,只用来自己回顾,大家感兴趣可以去看看大佬的文章,写的很详细
一、事件循环EventLoop
事件循环是asyncio的核心,异步任务的运行、任务完成之后的回调、网络IO操作、子进程的运行,都是通过事件循环完成的。我们不用管事件循环,只需要使用高层API,即asyncio中的方法,我们很少直接与事件循环打交道,但是为了更加熟悉asyncio的运行原理,最好还是了解EventLoop的设计原理。

1、事件循环的创建、获取、设置(上文已经介绍过了)

(1)asyncio.get_running_loop()。python3.7新添加的

(2)asyncio.get_event_loop()

(3)asyncio.set_event_loop(loop)

(4)asyncio.new_event_loop()

2、运行和停止事件循环

(1)loop.run_until_complete(future)。运行事件循环,直到future运行结束

(2)loop.run_forever()。在python3.7中已经取消了,表示事件循环会一直运行,直到遇到stop。

(3)loop.stop()。停止事件循环

(4)loop.is_running()。如果事件循环依然在运行,则返回True

(5)loop.is_closed()。如果事件循环已经close,则返回True

(6)loop.close()。关闭事件循环

3、创建Future和Task

(1)loop.create_future(coroutine) ,返回future对象

(2)loop.create_task(corootine) ,返回task对象

(3)loop.set_task_factory(factory)

(4)loop.get_task_factory()

4、事件循环的时钟

loop.time()。可以这么理解,事件循环内部也维护着一个时钟,可以查看事件循环现在运行的时间点是多少,就像普通的time.time()类似,它返回的是一个浮点数值,比如下面的代码。
5、计划执行回调函数(CallBacks)

(1)loop.call_later(delay, callback, *args, context=None)

首先简单的说一下它的含义,就是事件循环在delay多长时间之后才执行callback函数,它的返回值是asyncio.TimerHandle类的一个实例对象。

(2)loop.call_at(when, callback, *args, context=None)

即在某一个时刻进行调用计划的回调函数,第一个参数不再是delay而是when,表示一个绝对的时间点,结合前面的loop.time使用,它的使用方法和call_later()很类似。它的返回值是asyncio.TimerHandle类的一个实例对象。

(3)loop.call_soon(callback, *args, context=None)

在下一个迭代的时间循环中立刻调用回调函数,用法同上面。它的返回值是asyncio.Handle类的一个实例对象。

(4)loop.call_soon_threadsafe(callback, *args, context=None)

这是call_soon()函数的线程安全版本,计划回调函数必须在另一个线程中使用。

需要注意的是:上面的几个回调函数都只使用了“位置参数”哦,asyncio中,大部分的计划回调函数都不支持“关键字参数”,如果是想要使用关键字参数,则推荐使用functools.aprtial()对方法进一步包装
总结注意事项:

(1)CallBack函数只能够定义为同步方法,不能够定义为async方法,及不能使用async和@asyncio.coroutine修饰;

(2)每一个CallBack方法只会调用一次,如果在同一个时刻有另个CallBack方法需要调用,则他们的执行顺序是不确定的;

(3)注意使用functools.partial()去修饰带有关键字参数的CallBack方法;

(4)如何理解?对于一般的异步函数,我们需要将它放在时间循环里面,然后通过事件循环去循环调用它,而因为CallBack并不是异步函数,它是定义为普通的同步方法,所以不能够放在时间循环里面,但是如果我依然想要让事件循环去执行它怎么办呢?那就不放进事件循环,直接让事件循环“立即、稍后、在什么时候”去执行它不就行了嘛,call的含义就是“执行”。
二、底层API之Future
1、Future的定义概览

Future的本质是一个类。他表示的是异步操作的最终将要返回的结果,故而命名为Future,它不是线程安全的。Future对象是awaitable的,参见系类文章的前面,

class asyncio.Future(*, loop=None)

2、asyncio中关于Future的几个方法

(1)asyncio.isfuture(obj) 。判断一个对象是不是Future,注意python中一切皆对象哦,包括函数,当obj是下面几种情况时返回true:

asyncio.Future的实例对象
asyncio.Task的实例对象
一个具有 _asyncio_future_blocking 属性的对象
(2)asyncio.ensure_future(obj, *, loop=None)。将一个obj包装成Future

(3)asyncio.wrap_future(future, *, loop=None)

将concurrent.futures.Future对象包装成一个 asyncio.Future 对象。

3、Future对象的常用方法

(1)result()。返回Future执行的结果返回值

如果Future被执行完成,如果使用set_result()方法设置了一个结果,那个设置的value就会被返回;

如果Future被执行完成,如果使用set_exception()方法设置了一个异常,那么使用这个方法也会触发异常;

如果Future被取消了,那么使用这个方法会触发CancelledError异常;

如果Future的结果不可用或者是不可达,那么使用这个方法也会触发InvalidStateError异常;

(2)set_result(result)

标记Future已经执行完毕,并且设置它的返回值。

(3)set_exception(exception)

标记Future已经执行完毕,并且触发一个异常。

(4)done()

如果Future1执行完毕,则返回 True 。

(5)cancelled()

判断任务是否取消。

(6)add_done_callback(callback, *, context=None)

在Future完成之后,给它添加一个回调方法,这个方法就相当于是loop.call_soon()方法,参见前面,如下例子:

如果要回调带有关键字参数的函数,也需要使用partial方法哦。

(7)remove_done_callback(callback)

(8)cancel()

(9)exception()

(10)get_loop()。返回Future所绑定的事件循环
三、集中回答以下几个问题
通过前面的讲解,已经讲清楚了asyncio架构里面的一些基本东西,现在可以来集中回答以下一些常见的问题了,弄清楚这希尔问题,可以方便我们更加深入的理解协程。

1、很多个协程一起运行有创建新的线程吗?

协程运行时,都是在一个线程中运行的,没有创建新的线程
2、线程一定效率更高吗?

也不是绝对的,当然在一般情况下,异步方式的执行效率是更高的,那是不是一定会提高呢?也不一定,这与协程的调用方式是由密切关系的,在有很多个异步方式的时候,一定要尽量避免异步函数的直接调用,这和同步是没什么区别的,一定要通过事件循环loop,“让事件循环在各个异步函数之间不停游走”,这样才不会造成阻塞

3、协程会不会有阻塞呢?

异步方式依然会有阻塞的,当我们定义的很多个异步方法彼此之间有一来的时候,比如,我必须要等到函数1执行完毕,函数2需要用到函数1的返回值,如上面的例子2所示,就会造成阻塞,这也是异步编程的难点之一,如何合理配置这些资源,尽量减少函数之间的明确依赖,这是很重要的。

4、协程的4种状态

协程函数相比于一般的函数来说,我们可以将协程包装成任务Task,任务Task就在于可以跟踪它的状态,我就知道它具体执行到哪一步了,一般来说,协程函数具有4种状态,可以通过相关的模块进行查看,请参见前面的文章,他的四种状态为:

Pending
Running
Done
Cacelled
创建future的时候,task为pending,事件循环调用执行的时候当然就是running,调用完毕自然就是done,如果需要停止事件循环,中途需要取消,就需要先把task取消,即为cancelled。

四、多任务实现并发
python异步协程函数的最终目的是实现并发,这样才能提高工作效率。

我们经常看见下面这样的代码,即:

 tasks = asyncio.gather(*[task1,task2,task3])
 loop.run_until_complete(tasks)
 
#或者是
 tasks = asyncio.wait([task1,task2,task3])
 loop.run_until_complete(tasks)
 
#甚至可以写在一起,即
loop.run_until_complete(asyncio.gather(*[task1,task2,task3])
#或者是
asyncio.gather(asyncio.wait([task1,task2,task3]))

上面这些都是一些简单的应用,可以同时进行多任务,进行并发,但是如果我们每一个任务都有返回值,而且需要获取这些返回值,这样做显然还不够,还需要做进一步的处理。

asyncio实现并发的思想是一样的,只是实现的手段稍有区别,主要有以下几种实现方式:

(1)使用gather同时注册多个任务,实现并发

awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

注意事项:

gather的返回值是它所绑定的所有任务的执行结果,而且顺序是不变的,即返回的result的顺序和绑定的顺序是保持一致的。

除此之外,它是awaitable的,所以,如果需要获取多个任务的返回值,既然是awaitable的,就需要将它放在一个函数里面,
(2)使用wait可以同时注册多个任务,实现并发

await asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

它与gather不同的地方是他的参数是集合类型,而且他的返回类型是这样一个形式,即

(done, pending).   #返回dones是已经完成的任务,pending是未完成的任务,都是集合类型,不同的是每一个元素不再是返回值,而是某一个task哦,

相同的是它依然也是awaitable的
五、Future补充下一篇预告
1、Future补充

asyncio中的Future类是模仿concurrent.futures.Future类而设计的,关于concurrent.futures.Future,可以查阅相关的文档。它们之间的主要区别是:

(1)asyncio.Future对象是awaitable的,但是concurrent.futures.Future对象是不能够awaitable的;

(2)asyncio.Future.result()和asyncio.Future.exception()是不接受关键字参数timeout的;

(3)当Future没有完成的时候,asyncio.Future.result()和asyncio.Future.exception()将会触发一个InvalidStateError异常;

(4)使用asyncio.Future.add_done_callback()注册的回调函数不会立即执行,它可以使用loop.call_soon代替;

(5)asyncio里面的Future和concurrent.futures.wait()以及concurrent.futures.as_completed()是不兼容的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值