python学习

asyncio 初学记录 (一、协程和任务)

2020-07-28  本文已影响0人  二傻吧

1.asyncio.run()

可以单独运行协程
asyncio.run()

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())

结果

started at 10:25:16
hello
world
finished at 10:25:19

Process finished with exit code 0

2.asyncio.create_task()

asyncio.create_task() 函数用来并发运行作为 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')}")

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

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

asyncio.run(main())

结果:

started at 10:38:18
hello
world
finished at 10:38:20

Process finished with exit code 0

因为两个say_after()协程并发进行了,所以比之前少了1秒。

3.await

await 可用于可等待对象:协程、任务和future,这三个下面会介绍

(1)协程:用async 关键字定义的协程函数

import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

asyncio.run(main())

(2) 任务task

为了并发,将协程打包起来(或者说加入日程)之后的结果就叫任务
当一个协程通过 asyncio.create_task() 等函数被打包为一个 任务,该协程将自动排入日程准备立即运行:

async def nested():
    return 42

async def main():
    # Schedule nested() to run soon concurrently
    # with "main()".
    task = asyncio.create_task(nested())

    # "task" can now be used to cancel "nested()", or
    # can simply be awaited to wait until it is complete:
    await task

asyncio.run(main())

(3) futures

这部分还不太理解,后续补充:
一种异步操作的返回结果,是一种低级的可等待对象,一般用于有回调函数的地方,在应用层一般不推荐创建futures对象,
当future对象被等待的时候意味着,这个协程一直会等待直到这个future涉及的对象在其他地方处理完毕。

4. 协程的运行

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

这个方法每次调用时会自动创建一个循环,用新建的循环运行传入的协程,结束后自动关闭,
当一个线程中有其他的loop在运行时,这个方法不能被调用。
debug为true时为调试模式下进行。

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

创建任务,让协程并发
name不为空时,会使用 Task.set_name()设置任务名称

所有加入到任务里的协程将会在 get_running_loop()返回的循环中进行,如果当前的线程中没有正在运行的loop,则会抛出RuntimeError异常。
我理解到的这种异常情况有如下:一个线程只会有一个循环事件,而在碰到协程时会创建子循环事件,如果一个线程要被创建两个线程或者当一个协程在没有loop的线程中被执行时就会报这个错。
需要注意有以下:在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future() 函数。

In Python 3.7+

task = asyncio.create_task(coro())
...

This works in all Python versions but is less readable

task = asyncio.ensure_future(coro())

5. 睡眠

coroutine asyncio.sleep(delay, result=None, *, loop=None)
在py3.8以上中不推荐使用loop参数,loop参数将在3.10版本中被删除。
result参数可以添加回调内容,当sleep结束后,将会调用回调内容。

import asyncio
import datetime

async def display_date():
    loop = asyncio.get_running_loop()
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)

asyncio.run(display_date())

6. 并发进行

awaitable asyncio.gather(aws, loop=None, return_exceptions=False)
并发运行aws参数中的可等待对象。
如果aws中有协程,将自动创建task,将其加入task。
在所有aws可等待结果都运行完成后,将会返回一个结果列表,顺序和aws中传入时顺序一致
如果 return_exceptionsFalse (默认),所引发的首个异常会立即传播给等待 gather() 的任务。
aws* 序列中的其他可等待对象 不会被取消 并将继续运行。

如果 return_exceptionsTrue,异常会和成功的结果一样处理,并聚合至结果列表。

如果 gather() 被取消,所有被提交 (尚未完成) 的可等待对象也会 被取消

如果 aws 序列中的任一 Task 或 Future 对象 被取消,它将被当作引发了 CancelledError 一样处理 -- 在此情况下 gather() 调用 不会 被取消。这是为了防止一个已提交的 Task/Future 被取消导致其他 Tasks/Future 也被取消。
在此函数中aws不能是list。
同5中内容,loop参数将被移除。

注解

如果 return_exceptions 为 False,则在 gather() 被标记为已完成后取消它将不会取消任何已提交的可等待对象。 例如,在将一个异常传播给调用者之后,gather 可被标记为已完成,因此,在从 gather 捕获一个(由可等待对象所引发的)异常之后调用 gather.cancel() 将不会取消任何其他可等待对象。
在 3.7 版更改: 如果 gather 本身被取消,则无论 return_exceptions 取值为何,消息都会被传播。

补充:同样功能的还有asyncio.wait函数,后面就将进行补充。

上一篇下一篇

猜你喜欢

热点阅读