python异步
这是Python中asyncio库的基本概念入门。
如果您来过这里,那么您可能听说过异步,并发和并行性之类的词。 在开始使用asyncio之前,让我们快速地(通过示例)正确了解这些词的一些基本知识,以便为此基础打下坚实的基础。
并发是 就像在单个核心CPU上运行两个线程一样。 每个线程的指令都可以交错,但是在任何给定时间,两个线程中只有一个正在积极进行中。
并行性就像在多核CPU的不同核上同时运行两个线程一样。
重要的是要注意,并行性意味着并发,但并非相反。
异步是一个较高级别的编程概念,您可以在其中激发一些任务,并确定虽然没有该任务的结果,但最好还是做一些其他工作而不是等待。
当您异步执行操作时,根据定义,这意味着这些操作之间是并发的。
为什么要异步编程?
我们为什么要编写您所说的异步程序-因为它可以多次提高程序的性能。 假设您有一台运行应用程序的核心机器。 您收到一个请求,并且需要进行两个数据库查询才能满足该请求。 每个查询需要50毫秒的时间。 对于同步程序,只有在完成第一个请求后才发出第二个请求-总时间为100毫秒。 使用异步程序,您可以一个接一个地触发这两个查询-总时间为50毫秒。
异步
Asyncio就是用Python编写异步程序。 Asyncio是事件循环 , 任务和协同程序之间完美结合的美妙交响-它会让您哭泣。
事件循环
这就是一切的可能-一个简单的循环就这样。 嗯,不是那么简单 。 但是这是它的工作方式。 事件循环是交响曲的编排者。 它一个接一个地运行任务 。 在任何给定时间,只有一项任务正在运行。
可以想象,活动任务承受很大压力,因为其他任务正在等待轮换。 因此,当活动任务发出阻塞呼叫(例如网络请求)并且无法继续进行时,它将控制权交给事件循环,从而意识到其他一些任务可能会更好地利用事件循环的时间。 它还告诉事件循环它到底被阻止了什么,以便在网络响应到来时,事件循环可以考虑给它时间再次运行。
事件循环时间很宝贵。 如果您没有取得进展,则应该退出循环,以便其他人可以。 事件循环是进度的度量。
协程和任务
协程(合作程序)是交响曲的关键元素。 当协程无用时,正是协程及其合作性质使得放弃对事件循环的控制。 协程是子例程概念的有状态概括。
子例程是您良好的老式函数或方法。 您调用子例程来执行计算。 您可以再次调用它,但是在两次调用之间它不会保持状态。 每次调用都是全新的,并且执行相同的计算。
另一方面,协程是一个可爱的小状态小部件。 它看起来像一个子例程,但是在两次执行之间保持状态。 换句话说,当协程“返回”(产量控制)时,它只是意味着它已经暂停了执行(具有某种保存状态)。 因此,当您随后“调用”(给予控制)协程时,可以说协程已从保存状态恢复执行是正确的。
协程看起来像一个普通函数,但是在它们的行为上,它们是有状态的对象,具有
resume()
和pause()
—类似于方法。
在Python 3.5+中, 协程自身暂停的方式是使用await
关键字。 在协程内部,当您await
另一个协程时,您退出事件循环并安排等待的协程立即运行。 也就是说,在协程内部await other_coroutine
将使其暂停,并安排协程other_coroutine
立即运行。
请注意,事件循环不会抢占正在运行的协程。 只有协程可以停下来。
下面是一个非常简单的示例,说明协程如何相互配合(Python 3.5+) 。 在本示例中,我们将使用预定义的协程asyncio.sleep
来帮助我们模拟阻止任务,但是在现实世界中,它可以是任何内容,例如网络请求,数据库查询等。
请注意,该代码在单个线程中运行 ,但是输出将具有交错的打印语句。 发生这种情况的原因是,当协程被阻塞时,它会退出循环,以便另一个程序可以运行(是的!使用asyncio进行异步编程)。
python运行coroutine_example.py
需要注意的几点
- 调用协程定义不会执行它 。 它初始化一个协程对象 。 您需要
await
协程对象 ,而不是协程定义,如上面line 8
line 17
和line 17
所示。 - 事件循环运行任务 ,而不直接运行 协程对象 。 任务是协程对象的包装。 当您编写
await coroutine_object
,实际上是安排了一个包装器任务,使其立即在事件循环上运行。 -
asyncio.sleep
也是协程,由asyncio库提供。asyncio.sleep(2)
初始化一个协程对象,值为2秒。await
时,可以控制事件循环。 睡眠协程很聪明,不会阻塞循环。 它立即释放控制权,仅要求循环在指定时间后将其唤醒。 时间到期后,将其交还给控件,并立即返回,从而取消阻塞其调用方(在上面的示例中,coroutine_1
或coroutine_2
)。 - 上面的示例在事件循环上运行了三种不同类型的协程,即
coroutine_1
,coroutine_2
和asyncio.sleep
。 但是,四个不同的任务在循环上运行,分别对应于以下协程对象—预定在line 25
coroutine_1()
和coroutine_2()
,预定在line 8
asyncio.sleep(4)
和预定于line 17
asyncio.sleep(5)
。 - 在循环上调度任务(尽管不是立即)的另一种方法是使用
ensure_future()
或AbstractEventLoop.create_task()
方法,这两个方法都接受协程对象。 最后的示例代码演示了这些方法。
一个更现实而又简单的例子
python运行asyncio_example.py
ArchSaber的Python
在ArchSaber ,我们的目标之一一直是从客户的应用程序代码中深入挖掘见解。 我们的许多客户都依赖我们针对Python的APM解决方案。 结果,我们在理解语言及其周围框架的复杂性方面付出了巨大的努力。 我们自己严重依赖Python-我们的许多分析引擎和ML代码都是用Python编写的,通过它我们可以将实时根本原因分析推送到客户的生产问题。