当他们谈论程序执行时,“异步执行”是指程序不等待某个过程完成,而是继续独立于其工作的情况。 异步编程的一个示例是一个实用工具,它可以异步工作并写入日志文件。 尽管这样的实用程序可能会失败(例如,由于缺少可用磁盘空间),但是在大多数情况下,它会正常运行并可以在各种程序中使用。 他们将能够打电话给她,将记录的数据传递给她,然后他们就可以继续做自己的事情。

在编写某个程序时使用异步机制意味着该程序将比不使用此类机制运行得更快。 同时,应考虑紧急情况来编写计划异步启动的内容(例如用于日志记录的实用程序)。 例如,如果磁盘空间用完了,则用于记录日志的实用程序可以简单地停止记录,而不会因错误而“崩溃”主程序。
异步代码执行通常涉及在单独的线程中执行此类代码。 这是-如果我们正在谈论具有单核处理器的系统。 在具有多核处理器的系统上,此类代码很可能由使用独立内核的进程执行。 在给定时间,单核处理器只能读取和执行一条指令。 这就像看书。 您不能同时阅读两本书。
如果您正在阅读一本书,而其他人正在给您另一本书,那么您可以阅读第二本书并开始阅读。 但是第一个必须推迟。 多线程代码执行是按照相同的原理安排的。 而且,如果您的几本副本可以一次读几本书,那么它就类似于多处理器系统的工作方式。
如果在单核处理器上非常快速地在需要不同计算能力的任务之间进行切换(例如,在某些计算之间以及从磁盘读取数据之间),那么您可能会感觉到单个处理器核可以同时执行多项操作。 或者说,如果您尝试一次在浏览器中打开多个站点,则会发生这种情况。 如果浏览器使用单独的流来加载每个页面,则所有操作的完成速度将比这些页面一次加载的速度快得多。 加载页面并不是一件困难的任务,它不会最大限度地利用系统资源,因此,同时启动多个此类任务是非常有效的举动。
Python异步编程
最初,Python使用基于生成器的协程来解决异步编程任务。 然后,在Python 3.4中,
asyncio
了
asyncio
模块(有时其名称写为
async IO
),该模块实现了异步编程机制。 Python 3.5引入了async / await构造。
为了用Python进行异步开发,您需要处理几个概念。 这些是协程和任务。
协程
通常,协程是异步函数。 协程也可以是从协程函数返回的对象。
如果在声明一个函数时表明它是异步的,则可以使用
await
关键字来调用它:
await say_after(1, 'hello')
这样的构造意味着程序将一直执行直到遇到一个await-expression,之后它将调用该函数并暂停执行,直到被调用函数的工作完成。 之后,其他协程也将能够启动。
暂停程序意味着控件返回到事件循环。 当使用
asyncio
模块时,事件循环执行所有异步任务,执行I / O和执行子流程。 在大多数情况下,任务用于运行corutin。
任务
任务使您可以在事件循环中运行协程。 这简化了几个协程的执行控制。 这是使用协程和任务的示例。 请注意,使用
async def
构造声明的实体是协程。 此示例摘自Python的
官方文档 。
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}")
say_after()
函数具有
async
前缀;因此,我们有了协程。 如果我们从本示例中稍作改动,可以说可以像下面这样调用该函数:
await say_after(1, 'hello') await say_after(2, 'world')
但是,使用这种方法,协程会被顺序调用,大约需要3秒钟才能完成。 在我们的示例中,它们是竞争性推出的。 对于他们每个人都使用一个任务。 结果,整个程序的执行时间约为2秒。 请注意,要使这样的程序正常工作,仅使用
async
声明
main()
函数是不够的。 在这种情况下,您需要使用
asyncio
模块。
如果您运行示例代码,则屏幕上将显示类似于以下内容的文本:
started at 20:19:39 hello world finished at 20:19:41
请注意,第一行和最后一行中的时间戳相差2秒。 如果您按顺序调用corutin运行此示例,则时间戳之间的时差将已经是3秒。
例子
在此示例中,确定了计算数字序列的十个元素之和所需的运算次数。 从序列末尾开始进行计算。 递归函数首先获取数字10,然后使用数字9和8调用自身,将返回的值加起来。 这一直持续到计算完成为止。 结果,例如,从1到10的数字序列的总和为55。同时,我们的函数效率很低,这里使用
time.sleep(0.1)
构造。
这是功能代码:
import time def fib(n): global count count=count+1 time.sleep(0.1) if n > 1: return fib(n-1) + fib(n-2) return n start=time.time() global count count = 0 result = fib(10) print(result,count) print(time.time()-start)
如果您使用异步机制重写此代码并在
asyncio.gather
应用
asyncio.gather
构造,该构造负责执行两个任务并等待它们完成,会发生什么情况?
import asyncio,time async def fib(n): global count count=count+1 time.sleep(0.1) event_loop = asyncio.get_event_loop() if n > 1: task1 = asyncio.create_task(fib(n-1)) task2 = asyncio.create_task(fib(n-2)) await asyncio.gather(task1,task2) return task1.result()+task2.result() return n
实际上,此示例的工作速度甚至比上一个示例还要慢一些,因为所有内容都在一个线程中执行,并调用
create_task
,
gather
和其他类似的操作在系统上造成了额外的负担。 但是,此示例的目的是演示在多个任务中竞争并等待它们完成的能力。
总结
在某些情况下,使用任务和corutin非常有用,例如,如果程序包含输入输出和计算操作的混合,或者在同一程序中执行了不同的计算,则可以通过竞争性地运行代码而不是运行代码来解决这些问题。在顺序模式下。 这有助于减少程序执行某些操作所需的时间。 但是,这不允许例如同时执行计算。 多重处理用于组织此类计算。 这是一个单独的大话题。
亲爱的读者们! 您如何编写异步Python代码?
