原文出自:Getting Started with Asyncio in Python


Asyncio在3.4版本中成为Python生态系统的一部分,从那时起成为大量Python库和框架的基础,因为它具有令人印象深刻的速度和易用性。 Asyncio允许您轻松编写使用称为协程的单线程并发程序,这些协同程序就像一个简化的线程。

Asyncio还能很好地让从复杂的操作中抽身出来,例如通过套接字多路复用I / O访问,它还通过提供一组同步原语来简化我们的工作,使我们能够使程序具有线程安全性。


入门:

为了开始使用asyncio,我们需要一个关键组件,即事件循环。所有基于asyncio的系统都需要一个事件循环,这是我们程序性能的关键。事件循环调度我们的asyncio.coroutines并处理所有繁重的工作。

我们可以定义一个事件循环,只需在coroutine上执行,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import asyncio


async def my_coroutine():
    print("简单的事件循环实例")


def main():
    # 定义事件循环的实例
    loop = asyncio.get_event_loop()
    """
    告诉此事件循环运行,直到分配给它的所有任务完成。 在这个例子中只是执行my_coroutine()协程。
    """
    loop.run_until_complete(my_coroutine())
    # 通过调用close()来关闭我们的循环
    loop.close()


if __name__ == '__main__':
    main()

当你运行它时,你应该看到我们的my_coroutine()成功执行。

现在你要吐槽了,这似乎并不能给我们带来什么好处。只有额外的代码····

对于这个列子他似乎是没提供太多的好处,但是在更复杂的情况下。我们就会看到真正的性能优势。


我建议查看我在 aiohttp和Python中创建REST API 的教程。这提供了一个更复杂的示例,并且是高性能asyncio的一个很好的示例。


协程

因此,这些 coroutines 基本上是更传统线程的轻量级版本。通过使用这些,我们基本上使自己能够编写与线程非常相似的异步程序,但它们运行在单个线程之上。我们可以用两种不同的方式定义协同程序。

1
2
3
4
5
6
7
8
import asyncio

async def myFunc1():
    print("协程 1")

@asyncio.coroutine
def myFunc2()
    print("协程 2")

第一种方法是在Python 3.5中引入的,我倾向于建议你使用这种方法而不是后者。

Futures

Asyncio中的Futures 与在Python ThreadPoolExecutors 或者 ProcessPoolExecutors 看到的十分类似。并且遵循几乎相同的实现。

创建 Futures 对象的目的是为了在将来的某个时间给出一个结果,因此而得名。这是非常有好处的,因为这将意味着在您的Python程序中,您可以在等待 Future 返回结果时执行其他任务。

值得庆幸的是,在Asyncio中与 Futures 结合的方法非常容易。这样归功于 ensure_future() 方法,该方法接受一个协程并返回该协程的 Future 版本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import asyncio


# 定义一个协程,它接受一个future
async def my_coroutine(future):
    # 模拟一些'工作'
    await asyncio.sleep(1)
    # 设置结果给 future 对象
    future.set_result("Coroutine 转 Futures 已经完成")


async def main():
    # 定义一个 future 对象
    future = asyncio.Future()
    # 等待我们使用 ensure_future() 函数 将 coroutine 转换为 Futures 对象完成
    await asyncio.ensure_future(my_coroutine(future))
    # 打印 futures 的结果
    print(future.result())

# 启动快速简单的事件循环并运行直到完成
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.close()

如果 你运行了它,应该看到我们程序成功的将我们的协程转换为了 Futures 对象并打印出了结果。

多个协程

现在让我们尝试利用asyncio同时运行多个协同程序的能力。这将有助于您了解asyncio的强大功能以及如何使用它来有效地创建在单线程上运行的令人难以置信的高性能Python程序。

让我们从创建一个简单的协程开始,该协程将id作为其主要参数。 我们生成十个任务,然后给 await asyncio.gather() 函数传入我们的任务列表,它将等待这些任务完成。最后,我么将使用前一个示例中相同的事件循环来运行我们的Asyncio程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import asyncio
import random


async def my_coroutine(_id):
    process_time = random.randint(1, 5)
    await asyncio.sleep(process_time)
    print("协程: {}, 在 {} 秒后成功完成".format(_id, process_time))


async def main():
    tasks = []
    for i in range(10):
        tasks.append(asyncio.ensure_future(my_coroutine(i)))

    await asyncio.gather(*tasks)


loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.close()

我们的协程会同时执行并在不同的时间执行完成。需要重点注意,这些任务并没有按照提交的顺序完成。如果你执行上述程序,则需要5S才能完成执行。

结论:

这只是对asyncio框架的一个非常快速和简单的介绍。我们将在以后的教程中更详细地介绍这个框架。