python之协程

目录

[一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。](#一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。)

二、asyncio.run()内部则使用await关键字来调用。

三、并发协程asyncio.create_task()和asyncio.TaskGroup

四、可等待对象await

五、创建任务asyncio.create_task()

六、任务组TaskGroup

七、休眠asyncio.sleep()

八、并发运行任务asyncio.gather()

九、屏蔽取消操作asyncio.shield()

十、超时

十一、等待

十二、在线程中运行asyncio.to_thread()

十三、跨线程调度

十四、内省


一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。

python 复制代码
import asyncio


# 定义一个协程函数
async def main():
    print('Hello ...')
    return 'done'


if __name__ == '__main__':
    # 运行协程函数
    run = asyncio.run(main())
    print(run)

二、asyncio.run()内部则使用await关键字来调用。

python 复制代码
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())

三、并发协程asyncio.create_task()asyncio.TaskGroup

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

    # 等待两项任务都完成(两秒,而不是三秒)
    await task1
    await task2

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


if __name__ == '__main__':
    asyncio.run(main())
    print(f"finished at {time.strftime('%X')}")
python 复制代码
import asyncio
import time


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


async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(
            say_after(1, 'hello'))

        task2 = tg.create_task(
            say_after(2, 'world'))

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

    # 当上下文管理器退出时,等待是隐式的。
    print(f"finished at {time.strftime('%X')}")


if __name__ == '__main__':
    asyncio.run(main())
    print(f"finished at {time.strftime('%X')}")

四、可等待对象await

如果一个对象可以在 await 语句中使用,那么它就是 可等待 对象。许多 asyncio API 都被设计为接受可等待对象。

可等待 对象有三种主要类型: 协程, 任务Future.

python 复制代码
import asyncio


async def nested():
    return 42


# 协程属于 可等待 对象,因此可以在其他协程中被等待:
async def main():
    rs = await nested()
    print(rs)


asyncio.run(main())
python 复制代码
import asyncio


async def nested():
    return 42


async def main():
    # 将协程封装成一个任务,该协程会被自动调度执行:
    task = await asyncio.create_task(nested())
    print(task)

# 任务 被用来"并行的"调度协程
asyncio.run(main())
python 复制代码
import asyncio


async def set_after(fut, delay, value):
    await asyncio.sleep(delay)
    fut.set_result(value)


async def main():
    loop = asyncio.get_running_loop()
    fut = loop.create_future()

    loop.create_task(
        set_after(fut, 1, '... world'))

    print('hello ...')
    print(await fut)


asyncio.run(main())

五、创建任务asyncio.create_task()

事件循环将只保留对任务的弱引用。 未在其他地方被引用的任务可能在任何时候被作为垃圾回收,即使是在它被完成之前。 如果需要可靠的"发射后不用管"后台任务,请将它们放到一个集合中。

python 复制代码
import asyncio


async def some_coro(param):
    await asyncio.sleep(1)
    print(param)


async def main():
    # 存储引用
    background_tasks = set()

    for i in range(10):
        task = asyncio.create_task(some_coro(param=i))

        # 保存一个指向此函数的结果的引用,以避免任务在执行过程中消失。 事件循环将只保留对任务的弱引用。 未在其他地方被引用的任务可能在任何时候被作为垃圾回收,即使是在它被完成之前。 如果需要可靠的"发射后不用管"后台任务,请将它们放到一个多项集中
        background_tasks.add(task)

        # 为了防止永远保留对已完成任务的引用,
        # 每个任务在完成后进行回调,自己从集合中删除自己的引用。
        task.add_done_callback(background_tasks.discard)

    # 等待所有任务完成
    await asyncio.gather(*background_tasks)


if __name__ == '__main__':
    asyncio.run(main())

六、任务组TaskGroup

任务组 合并了一套用于等待分组中所有任务完成的方便可靠方式的任务创建 API。

在等待期间,仍可将新任务添加到分组中 (例如,通过将 tg 传入某个协程并在该协程中调用 tg.create_task())。 一旦最后的任务完成并退出 async with 代码块,将无法再向分组添加新任务。

python 复制代码
import asyncio


async def some_coro(param):
    await asyncio.sleep(1)
    return param


async def another_coro(tg):
    await asyncio.sleep(1)
    # 在执行过程中向任务组中添加新的任务
    tg.create_task(some_coro(2))
    return 3


async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(some_coro(1))
        task2 = tg.create_task(another_coro(tg))
    print(f"任务完成: {task1.result()}, {task2.result()}")


if __name__ == '__main__':
    asyncio.run(main())

七、休眠asyncio.sleep()

sleep() 总是会挂起当前任务,以允许其他任务运行。将 delay 设为 0 将提供一个经优化的路径以允许其他任务运行。 这可供长期间运行的函数使用以避免在函数调用的全过程中阻塞事件循环。

python 复制代码
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
        # 休眠1秒
        await asyncio.sleep(1)


asyncio.run(display_date())

八、并发运行任务asyncio.gather()

一个创建然后并发地运行任务等待它们完成的新选择是 asyncio.TaskGroupTaskGroup 提供了针对调度嵌套子任务的比 gather 更强的安全保证:如果一个任务(或子任务,即由一个任务调度的任务)引发了异常,TaskGroup 将取消剩余的已排期任务)。

python 复制代码
import asyncio

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


async def main():
    # 并行运行三个任务
    L = await asyncio.gather(
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
    )
    print(L)


asyncio.run(main())

九、屏蔽取消操作asyncio.shield()

保护一个 可等待对象 防止其被 取消

python 复制代码
import asyncio
from asyncio import shield


async def something():
    pass


async def main():
    # 任务
    task = asyncio.create_task(something())
    # 防止任务被取消
    res = await shield(task)
    # 相当于
    # res = await something()


asyncio.run(main())

十、超时

asyncio.timeout()

返回一个可被用于限制等待某个操作所耗费时间的 异步上下文管理器

python 复制代码
import asyncio


async def something():
    await asyncio.sleep(6)


async def main():
    # 超时的抛出异常
    async with asyncio.timeout(5):
        await something()


asyncio.run(main())
python 复制代码
import asyncio


async def something():
    await asyncio.sleep(6)


async def main():
    try:
        # 我们不知道启动时的超时时间,填None。
        async with asyncio.timeout(None) as cm:
            # 当我们知道到超时时间,我们调用reschedule方法填入
            new_deadline = asyncio.get_running_loop().time() + 4
            cm.reschedule(new_deadline)
            await something()
    except TimeoutError:
        pass

    if cm.expired():
        print("超时了没有响应")


asyncio.run(main())

asyncio.timeout_at(when)

类似于 asyncio.timeout(),不同之处在于 when 是停止等待的绝对时间,或者为 None

python 复制代码
import asyncio


async def something():
    await asyncio.sleep(6)


async def main():
    loop = asyncio.get_running_loop()
    deadline = loop.time() + 3
    try:
        async with asyncio.timeout_at(deadline):
            await something()
    except TimeoutError:
        print("处理超时了")

    print("继续执行")


asyncio.run(main())

asyncio.wait_for(aw , timeout)

等待 aw 可等待对象 完成,指定 timeout 秒数后超时。

如果发生超时,将取消任务并引发 TimeoutError

python 复制代码
import asyncio


async def eternity():
    await asyncio.sleep(3600)
    print('yay!')


async def main():
    try:
        await asyncio.wait_for(eternity(), timeout=1.0)
    except TimeoutError:
        print('超时!')


asyncio.run(main())

十一、等待

asyncio.wait(aws , * , timeout=None , return_when=ALL_COMPLETED)

并发地运行 aws 可迭代对象中的 FutureTask 实例并进入阻塞状态直到满足 return_when 所指定的条件。aws 可迭代对象必须不为空。返回两个 Task/Future 集合: (done, pending)

wait_for() 不同,wait() 在超时发生时不会取消可等待对象。

python 复制代码
import asyncio


async def eternity():
    await asyncio.sleep(1)
    print('yay!')


async def main():
    # 将协程对象转换为任务对象
    task = asyncio.create_task(eternity())
    done, pending = await asyncio.wait([task], timeout=2)  # 设置超时时间为 2 秒
    if pending:
        print("Some tasks are still pending.")


asyncio.run(main())

asyncio.as_completed(aws , * , timeout=None)

并发运行aws-iterable中的可唤醒对象。返回协程的迭代器。可以等待返回的每个协程,以从剩余的可迭代对象中获得最早的下一个结果。

python 复制代码
import asyncio


async def eternity():
    await asyncio.sleep(1)
    print('yay!')


async def main():
    for coro in asyncio.as_completed([eternity()]):
        earliest_result = await coro


asyncio.run(main())

十二、在线程中运行asyncio.to_thread()

在不同的线程中异步地运行函数func 。允许在不同的线程中访问来自事件循环的上下文变量。返回一个可被等待以获取 func 的最终结果的协程。

python 复制代码
import asyncio
import time


async def eternity():
    await asyncio.sleep(1)
    return "异步IO完成"


def blocking_io():
    time.sleep(1)
    return "普通IO完成"


async def main():
    result = await asyncio.gather(
        # 将普通函数转换为异步任务
        asyncio.to_thread(blocking_io),
        asyncio.sleep(1),
        eternity())
    print(result)


asyncio.run(main())

十三、跨线程调度

向指定事件循环提交一个协程。(线程安全)

python 复制代码
import asyncio

async def main():
    # 定义协程
    coro = asyncio.sleep(1, result=3)

    # 获取当前运行的事件循环
    loop = asyncio.get_running_loop()

    # 使用 run_coroutine_threadsafe 在当前事件循环中运行协程
    future = asyncio.run_coroutine_threadsafe(coro, loop)

    # 使用 asyncio.wrap_future 将 concurrent.futures.Future 转换为 asyncio.Future
    wrapped_future = asyncio.wrap_future(future)

    # 等待结果
    result = await wrapped_future
    print(result)

# 在主线程中运行 main 协程
asyncio.run(main())

十四、内省

asyncio.current_task(loop=None)

返回当前运行的 Task 实例,如果没有正在运行的任务则返回 None。如果 loopNone 则会使用 get_running_loop() 获取当前事件循环。

asyncio.all_tasks(loop=None)

返回事件循环所运行的未完成的 Task 对象的集合。如果 loopNone 则会使用 get_running_loop() 获取当前事件循环。

asyncio.iscoroutine(obj)

如果 obj 是一个协程对象则返回 True。如果 loopNone 则会使用 get_running_loop() 获取当前事件循环。

十五、Task 对象

Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。

python 复制代码
import asyncio


async def cancel_me():
    try:
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        raise
    finally:
        print('cancel_me(): after sleep')


async def main():
    task = asyncio.create_task(cancel_me())
    # 等待一秒
    await asyncio.sleep(1)
    # 取消 task
    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("main(): cancel_me is cancelled now")


asyncio.run(main())
相关推荐
三玖诶6 分钟前
在 Qt 中使用 QLabel 设置 GIF 动态背景
开发语言·qt·命令模式
akhfuiigabv13 分钟前
使用Neo4j-Cypher-FT实现自然语言查询图数据库
数据库·python·oracle·neo4j
繁依Fanyi32 分钟前
828华为云征文|华为Flexus云服务器搭建OnlyOffice私有化在线办公套件
服务器·开发语言·前端·python·算法·华为·华为云
zhangfeng113334 分钟前
在 PyTorch 中,除了 pad_sequence 还有哪些其他处理序列数据的函数?时间序列数据 预处理
人工智能·pytorch·python·深度学习
邂逅自己35 分钟前
Java入门程序-HelloWorld
java·开发语言
叫我小鹏呀40 分钟前
vue3中el-table中点击图片放大时,被表格覆盖
前端·javascript·vue.js
我命由我123451 小时前
2.使用 VSCode 过程中的英语积累 - Edit 菜单(每一次重点积累 5 个单词)
前端·javascript·ide·vscode·学习·编辑器·学习方法
python1561 小时前
Python Numpy布尔数组在数据分析中的应用
python·数据分析·numpy
AIAdvocate1 小时前
力扣-96.不同的二叉搜索树 题目详解
python·算法·动态规划
luthane1 小时前
python 实现entropy熵算法
python·算法·概率论