我正在使用异步任务对第 3 方库进行多次调用,该任务封装在函数 run_task 中,该函数是 CPU 密集型的。
我尝试使用 asyncio.to_thread 在单独的线程中运行它,但出现错误
这是我的代码的简化、正确版本,使用 shazamio。
import asyncio
from shazamio import Shazam
async def run_task(shazam):
ret = await asyncio.to_thread(shazam.recognize_song, '01 - Forest Drive West - Impulse.mp3')
print(ret)
return 1
async def run_all_tasks(iters):
shazam = Shazam()
loop = asyncio.get_event_loop()
coros = [run_task(shazam) for i in range(iters)]
await asyncio.gather(*coros)
return
if __name__ == '__main__':
asyncio.run(run_all_tasks(10))
有了这个,我得到 ret 是一个协程,所以等待不起作用。我也收到这些警告
<coroutine object Shazam.recognize_song at 0x11054bf10>
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py:1936: RuntimeWarning: coroutine 'Shazam.recognize_song' was never awaited
handle = self._ready.popleft()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
您收到的错误表明
.recognize_song
本身已经被编写为协同例程。 (确实如此,如文档中所示:https://github.com/dotX12/ShazamIO)
另一方面。
.to_thread
调用期望接收常规Python函数,它将生成另一个线程并在那里运行(并使用某种内部机制检索返回值)。
发生的事情是调用
.to_thread
,它调用协同例程方法 .recognize_song
,该方法立即返回一个可等待对象 - .to_thread
不检查它,并将其作为调用“的结果返回”阻止”方法。
这种情况下的修复很简单:只需直接等待
shazam.recognize_song
调用即可。
顺便说一句,这不是本地的“CPU 密集型任务”:它所做的是使用 Shazam API 远程识别您的歌曲 - 它尽可能地受 I/O 限制。是的,远程 API 将执行 CPU 密集型工作,但在主机数据中心的远程 CPU 上。