python asyncio as_completed的疑惑

在阅读了相当多关于 asyncio 的内容之后(我对它完全是菜鸟),我已经成功编写了一些简单的程序来完成我想要它们做的事情。

然而,我对 as_completed 方法有一些疑问:它的内部工作原理以及它如何影响我的 CPU 使用率。

因此,让有以下片段:

#as_completed_example.py

import asyncio

import tqdm

import datetime

import sys

import signal

import random

#--------------------------------------------------------------------------------------

async def heavy_load(i):

    #tqdm.tqdm.write('#DEBUG    '+datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+' '+str(i))

    await asyncio.sleep(random.random())

    return None

#--------------------------------------------------------------------------------------

async def main():

    length  =   int(sys.argv[1])

    inputs  =   list(range(length))

    pbar    =   tqdm.tqdm(total=len(inputs),position=0,leave=True,bar_format='#PROGRESS {desc}: {percentage:.3f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}')

    tasks   =   [heavy_load(i) for i in inputs]

    for future in asyncio.as_completed(tasks):

        _ = await future

        pbar.update(1)

        pbar.refresh()            

#---------------------------------------------------------------------------    

def sigint_handler(signum,frame):

    tqdm.tqdm.write('#INFO '+datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+' user aborted execution!')

    sys.exit(0)

#---------------------------------------------------------------------------    

if(__name__=='__main__'):

    signal.signal(signal.SIGINT,sigint_handler)

    asyncio.run(main())     

如果我将其称为 python3 as_completed_example.py 1000,它会完美地工作。然而,如果我将其称为as_completed_example.py 1000000 (large number),我会观察到我的进度条在相当长的一段时间内卡在 0% :

-虽然我的进度条为 0%,

--我的 CPU 发生了什么?因为需要一个核心才能达到 100% 使用率

-为什么我future在相当长一段时间后没有从 as_completed得到任何信息?


慕无忌1623718
浏览 143回答 1
1回答

猛跑小猪

这里有几个问题,如果可能的话应该避免。但对于你的前两个问题有一个简单的答案。为什么它固定单个CPU,为什么在打印进度条之前有延迟?有很多单线程工作要做。asyncio在单个线程中运行所有内容,除非您明确这样做。您正在构建的任务需要在 内部进行大量设置asyncio,尤其是对as_completed. 但它必须:创造set你的未来。不太贵,但不是免费的。设置生产者和消费者队列来控制尚未运行的任务和已完成的任务。对于您正在使用的如此大量的任务,这可能会导致多次、大量的分配,这可能是一个真正的杀手。安排回调在 future 完成时运行。这主要是将它们从队列移动到另一个队列,并将它们从setfuture 中删除,这些都不是免费的。成就每一个未来事实上,这里有很多设置,并且这需要相当多的时间,通过改变输入的大小可以很容易地看到这一点。在我的笔记本电脑上,运行任何期货之前的时间确实从 size 开始急剧下降100000。此外,它会非线性下降,这表明这个大小对于我的机器上的内存层次结构特别不利(例如,在这个大小之后,会有更多的缓存未命中)。asyncio我还发现事件循环的解析可能在这里发挥了作用。期货的消耗必须经过一定的时间。您正在创建许多 future,其中许多几乎是在事件循环的单个滴答内同时完成的(正如 @user4815162342 在评论中正确指出的那样)。事件循环的每个周期都有大量工作,并且必须全部在单个线程上完成。考虑到整个事情需要超过 1 秒才能完成这一事实,这一点非常清楚。最大睡眠间隔为 1 秒,因为random.random为您提供了 上的值[0, 1.0),但整个应用程序需要更长的时间。因此,这里正在进行的工作不仅仅是“一秒钟的价值”,而且全部都在一个线程中进行。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python