Python学习笔记:协程初探

对着背影说爱祢 提交于 2020-01-26 03:43:57

(本文主要参照Python在线文档进行学习)

0.前言

进程有自己独立的堆和栈,由操作系统调度;线程有独立的栈,共享堆空间,标准的线程也是由操作系统调度;协程和线程一样共享堆不共享栈,但是是由程序员在协程的代码里显示调度。协程避免了无意义的调度,进而提升性能,但也增加了程序员的责任。

1.初探

asyncio是一个使用 async / await 语法编写并发代码的库,在Python 3.4 引入,直接内置了对异步IO的支持,并在后来的版本中不断完善。早期基于生成器来实现协程,但是在 Python 3.8 开始不推荐使用该方式,并将在 3.10 移除。目前标准的是使用 async / await 语法来实现协程。

要实际运行协程,asyncio 提供了三种主要机制:

a.使用 asyncio.run() 函数来运行顶层入口点

(运行代码需要 Python3.7 ;要注意的是,直接调用 func() 并不能执行)

import asyncio
import time

async def func():
    print('func in'+time.strftime('%X'))
    await asyncio.sleep(3)
    print('func out'+time.strftime('%X'))

print('begin'+time.strftime('%X'))
asyncio.run(func())
print('end'+time.strftime('%X'))

b.等待协程

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")
    await say_after(1, 'hello')
    await say_after(2, 'world')
    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

c.用asyncio.create_task() 函数把协程作为异步任务来同时执行

(修改上个示例的 main 函数)

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')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

2.可等待的对象 awaitable

如果可以在 await 表达式中使用,则这个对象是 awaitable 的。可等待的对象主要有三种:协程coroutines,任务Tasks,期望Futures。

a.协程coroutines

Python 协程是可等待的,并且可以等待自其他协程:

import asyncio

async def nested():
    return 42

async def main():
    # 一个协程对象创建了但是没有被等待,不会执行
    print(nested())
    # 输出 "42".
    print(await nested())  

asyncio.run(main())

 

协程相关的两个重要概念:协程函数,通过 async def 定义的函数;协程对象,调用协程函数返回的对象。

b.任务Tasks

任务可以用来调度协程并发执行。当使用 asyncio.create_task() 打包一个协程,该协程很快会自动计划运行。

import asyncio

async def nested():
    return 42

async def main():
    # nested() 与 main() 会同时运行
    task = asyncio.create_task(nested())
    # "task" 现在可用于取消 "nested()", 或是等待其完成
    await task

asyncio.run(main())

c.期望Futures

Futures 是特殊的低级可等待对象,代表异步操作的最终结果。当 await 一个 Futures 时,协程将等待直到 Futures 在别的地方完成。一般不需要在应用中创建 Futures 对象。

3.部分API参考

a.运行一个异步程序

asyncio.run(coro, *, debug = False )

执行协程 coro 并返回结果。它应该用作 asyncio 程序的主要入口点,理想情况下应该只调用一次。

b.创建任务

asyncio.create_task(coro, *, name = None )

将协程 coro 包装为一个 Task 并调度执行,返回一个 Task 对象。

c.休眠

asyncio.sleep(delay, result=None, *, loop=None)

阻塞等待 delay 秒,sleep 挂起当前任务,允许其他任务运行。

d.同时运行

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

并发执行 aws 列表中的可等待对象。如果 aws 中有任何可等待的协程,将自动作为一个 Task 调度。

import asyncio

async def factorial(name, number):
    f = 1
    for i in range(2, number + 1):
        print(f"Task {name}: Compute factorial({i})...")
        await asyncio.sleep(1)
        f *= i
    print(f"Task {name}: factorial({number}) = {f}")

async def main():
    # Schedule three calls *concurrently*:
    await asyncio.gather(
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
    )

asyncio.run(main())

# Expected output:
#
#     Task A: Compute factorial(2)...
#     Task B: Compute factorial(2)...
#     Task C: Compute factorial(2)...
#     Task A: factorial(2) = 2
#     Task B: Compute factorial(3)...
#     Task C: Compute factorial(3)...
#     Task B: factorial(3) = 6
#     Task C: Compute factorial(4)...
#     Task C: factorial(4) = 24

e.超时等待

asyncio.wait_for(aw, timeout, *, loop=None)

aw 是一个协程,如果 timeout 秒数为 None,将会阻塞直到完成。

async def eternity():
    # Sleep for one hour
    await asyncio.sleep(3600)
    print('yay!')

async def main():
    # Wait for at most 1 second
    try:
        await asyncio.wait_for(eternity(), timeout=1.0)
    except asyncio.TimeoutError:
        print('timeout!')

asyncio.run(main())

# Expected output:
#
#     timeout!

协程相关的接口还是挺多的,这里只列举了部分,详情可以参照书籍或文档 

4.参考:

Python在线文档:https://docs.python.org/3/library/asyncio-task.html

百度:https://wenku.baidu.com/view/a52ac722a22d7375a417866fb84ae45c3b35c24e.html

博客:https://www.jianshu.com/p/e38ee24dd004

博客:https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!