等待python中的非异步函数

krcsximq  于 2023-03-09  发布在  Python
关注(0)|答案(2)|浏览(146)

我在看一些asyncio包的python教程,下面是一个典型的例子:

import asyncio

async def first_function():
    print("Before")
    await asyncio.sleep(1)
    print("After")

async def second_function():
    print("Hello")

async def main():
    await asyncio.gather(first_function(), second_function())

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

即使在更复杂的例子中,最里面的异步函数也是asyncio.sleep或者来自某个“异步库”的函数。但是这些函数是如何组成非异步函数的呢?假设我有一个来自外部库的非异步函数,它的执行时间很长long_function。(这个函数不包含任何await,例如,它可能是用另一种语言编写的)。我如何从它编写一个异步函数 Package 器(也许将来会有),以便我可以编写

import asyncio

async def long_function_wrapper():
     ...
     #something with long_function()
     ...

async def first_function():
    print("Before")
    await long_functin_wrapper()
    print("After")

async def second_function():
    print("Hello")

async def main():
    await asyncio.gather(waiting_function(), short_function())

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

行为与前一个示例相同?(即输出顺序相同)

uxhixvfz

uxhixvfz1#

所有异步原语(asyncio.sleepaiohttp.get等)都基于通常由操作系统提供的低级并发原语,因此通常不包括其他嵌套的async函数调用。
这些方法依赖于线程、锁(互斥锁)、操作系统计时器、网络端口监听等。简而言之,当任务完成时,操作系统会通知程序。这个答案也可能是相关的。
创建异步函数的方法有很多种,最明显的一种是组合其他的异步函数,如果你想创建的异步函数是IO绑定的(网络,文件阅读,睡眠等),你可以 Package 在asyncio.to_thread中,它将在一个单独的线程中运行它:

import asyncio
import requests

def io_bound_task():
  return requests.get("https://some/url.com")

async def main():
  return await asyncio.to_thread(io_bound_task)

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

如果您的任务是CPU绑定的(繁重的计算),那么您将希望将其卸载到线程池(对于GIL释放任务)或进程池(对于其他任务):

import asyncio
from concurrent.futures import ProcessPoolExecutor
from math import sqrt

def cpu_bound_task():
  result = 0
  for _ in range(100_000_000):
    result = sqrt(result + 1)
  return result

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

  with ProcessPoolExecutor() as p:
    return await loop.run_in_executor(p, cpu_bound_task)

if __name__ == "__main__":
  asyncio.run(main())
epggiuax

epggiuax2#

如果您对async/await语法背后的实际概念感兴趣,我建议您参考PEP 492。要更深入地理解这一点,您应该熟悉协程以及其背后的迭代器背后的思想。您应该理解event loop(以及更广泛的调度器)的用途,以及它如何与迭代器/生成器联系在一起。
从技术上讲,解构现有的“常规”函数并将其转换为协程是可能的,但这并非易事。一般来说,由于事件循环在一个“单”线程中运行,除非您的long_function是异步的(即在某个点将控制权交还给调度程序),否则在async上下文中执行它将阻塞事件循环,直到它完成为止。
有一些第三方库尝试执行您所描述的操作,即“async-ify”非async函数,但我无法说明这些函数在实践中的效果如何,以及它们有哪些局限性。
如果你无法控制long_function,它是非async的,并且你想让它和其他东西并发执行,你最好在一个单独的线程或进程中运行它(取决于它做什么),Python中这三个内置并发选项之间的区别已经在本网站和其他地方详细讨论过了。
下面是一个示例:
multiprocessing vs multithreading vs asyncio

相关问题