Python 3 asyncio的一些用法

本代码基于Python3.8版本

在同步函数中调用异步函数

在同步函数中调用异步函数

协程只能在「事件循环」内被执行,且同一时刻只能有一个协程被执行。

在同步函数中调用异步函数,其本质就是将协程「扔进」事件循环中,等待该协程执行完获取结果即可。

以下这些函数,都可以实现这个效果:

asyncio.run
asyncio.run_coroutine_threadsafe
loop.run_until_complete
create_task

1、asyncio.run

import asyncio

async def do_work():
    return 1

def main():
    result = asyncio.run(do_work())
    print(result)  # 1

if __name__ == "__main__":
    main()

这种情况通常都是直接运行就可以得到结果了

asyncio.run 每次调用都会新开一个事件循环,当结束时自动关闭该事件循环

一个线程内只存在一个事件循环,所以如果当前线程已经有存在的事件循环了,就不应该使用 asyncio.run ,否则就会抛出如下异常:

 RuntimeError: asyncio.run() cannot be called from a running event loop

2、asyncio.run_coroutine_threadsafe

向指定事件循环提交一个协程。(线程安全,线程不安全的方法: run_coroutine)

返回一个 concurrent.futures.Future 以等待来自其他 OS 线程的结果。

换句话说,就是将协程丢给其他线程中的事件循环去运行。

值得注意的是这里的「事件循环」应该是其他线程中的事件循环,非当前线程的事件循环。

其返回的结果是一个 future 对象,如果你需要获取协程的执行结果可以使用 future.result() 获取,关于 future 对象的更多介绍,见 https://docs.python.org/zh-cn/3/library/concurrent.futures.html#concurrent.futures.Future

import asyncio
import threading
import time

loop = None


def get_loop():
    global loop
    if loop is None:
        loop = asyncio.new_event_loop()
    return loop


def another_thread():
    async def coro_func():
        return 1

    loop = get_loop()
    # 将协程提交到另一个线程的事件循环中执行
    future = asyncio.run_coroutine_threadsafe(coro_func(), loop)
    # 等待协程执行结果
    print(future.result())
    # 停止事件循环
    loop.call_soon_threadsafe(loop.stop)


def thread_with_loop():
    loop = get_loop()
    # 启动事件循环,确保事件循环不会退出,直到 loop.stop() 被调用
    loop.run_forever()
    loop.close()


# 启动一个线程,线程内部启动了一个事件循环
threading.Thread(target=thread_with_loop).start()
time.sleep(1)
# 在主线程中启动一个协程, 并将协程提交到另一个线程的事件循环中执行
t = threading.Thread(target=another_thread)
t.start()
t.join()

3、loop.run_until_complete

运行直到 future ( Future 的实例 ) 被完成,这个方法和 asyncio.run 类似。

具体就是传入一个协程对象或者任务,然后可以直接拿到协程的返回值。

run_until_complete 属于 loop 对象的方法,所以这个方法的使用前提是有一个事件循环,注意这个事件循环必须是非运行状态,如果是运行中就会抛出如下异常:

 RuntimeError: This event loop is already running

例子:

loop = asyncio.new_event_loop()
loop.run_until_complete(do_async_work())

4、create_task

要运行一个协程函数的本质是将携带协程函数的任务提交至事件循环中,由事件循环发现、调度并执行。

其实一共就是满足两个条件:

  • 任务
  • 事件循环

我们使用 async def func 定义的函数叫做协程函数,func() 这样调用之后返回的结果是协程对象,到这一步协程函数内的代码都没有被执行,直到协程对象被包装成了任务,事件循环才会“正眼看它们”。
所以事件循环调度运行的基本单元就是任务,那为什么我们在使用 async/await 这些语句时没有涉及到任务这个概念呢?

这是因为 await 语法糖在内部将协程对象封装成了任务,再次强调事件循环只认识任务。

所以,想要运行一个协程对象,其实就是将协程对象封装成一个任务,至于事件循环是如何发现、调度和执行的,这个我们不用关心。

那将协程封装成的任务的方法有

asyncio.create_task
asyncio.ensure_future
loop.create_task

看着有好几个,我们只关心 loop.create_task,因为其他方法最终都是调用 loop.create_task

async def do_work():
    return 222

task = loop.create_task(do_work())

do_work 会被异步执行,那么 do_work 的结果怎么获取呢,task.result() 可以吗?

分情况:

如果是在一个协程函数内使用 await task.result(),这是可以的;
如果是在普通函数内则不行。你不可能立即获得协程函数的返回值,因为协程函数还没有被执行呢。
asyncio.Task 运行使用 add_done_callback 添加完成时的回调函数,所以我们可以「曲线救国」,使用回调函数将结果添加到队列、Future 等等。

基于 concurrent.futures.Future 获取结果的例子

import asyncio
from asyncio import Task
from concurrent.futures import Future

from fastapi import FastAPI

app = FastAPI()
loop = asyncio.get_event_loop()


async def do_work1():
    return 222


@app.get("/")
def root():
    # 新建一个 future 对象,用于接受结果值
    future = Future()

    # 提交任务至事件循环
    task = loop.create_task(do_work1())

    # 回调函数
    def done_callback(task: Task):
        # 设置结果
        future.set_result(task.result())

    # 为这个任务添加回调函数
    task.add_done_callback(done_callback)

    # future.result 会被阻塞,直到有结果返回为止
    return future.result()  # 222

总结
协程函数中调用同步函数
核心思想:为了避免同步函数的执行阻塞当前线程(当前线程中的事件循环),应该由其他线程或进程执行后获取结果。

同步函数中调用协程函数
核心思想:任务协程对象都需要依赖事件循环。
所以要么启动新的事件循环以执行协程,要么依赖已有的事件循环, 至于实现方式就根据实际情况而选择。

《Python 3 asyncio的一些用法》为 九城 原创,创作不易!转载请注明出处!感谢!
文章地址:https://blog.minkse.cn/python-3-asyncio%e7%9a%84%e4%b8%80%e4%ba%9b/
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇