如何使用asyncio并行化块代码

2024-05-06 14:51:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道asyncio在StackOverflow中有很多特性,但是尽管这里回答了很多问题,我仍然不明白如何做一些简单的事情,比如并行化2个执行阻塞代码的任务

例如,这种方法非常有效:

import asyncio


async def slow_thing():
    await asyncio.sleep(2)


async def try_alpha():
    print("Alpha start")
    await slow_thing()
    print("Alpha stop")
    return "Alpha"


async def try_bravo():
    print("Bravo start")
    await slow_thing()
    print("Bravo stop")
    return "Bravo"


async def main():
    futures = [
        try_alpha(),
        try_bravo(),
    ]
    for response in await asyncio.gather(*futures):
        print(response)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

输出正是我想要的:

Alpha start
Bravo start
*2 second wait*
Alpha stop
Bravo stop
Alpha
Bravo

但是,如果我将await syncio.sleep(2)time.sleep(2)交换,输出就好像我的代码没有任何异步性一样:

Alpha start
*2 second wait*
Alpha stop
Bravo start
*2 second wait*
Bravo stop
Alpha
Bravo

问题是,在我的真实示例中,我无法控制缓慢的代码,因此我无法将其更改为使用协同程序。在某些情况下,它只是requests.get()的一系列用法,而在另一些情况下,我使用的是kodijson库,它做了许多我无法访问的事情

所以我想知道asyncio是否是这里的正确工具。在尝试与.gather()并行时,是否可以在异步代码中使用阻塞代码

还请注意,我(不幸地)在这一点上坚持使用Python3.6。我正在写一个Mycroft扩展,这就是他们目前所处的环境


Tags: 代码alphaloopasyncioasyncdefsleepawait
2条回答

在这里收到以评论形式提供的帮助后,我能够使用concurrent.futures将解决方案汇总在一起:

import concurrent.futures
import time


def slow_1(s):
    time.sleep(5)
    print(f"1: {s}")
    return "1: ok"


def slow_2(s):
    time.sleep(1)
    print(f"2: {s}")
    return "2: ok"


def slow_3(s):
    time.sleep(1)
    print(f"3: {s}")
    return "3: ok"


with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futures = (
        executor.submit(slow_1, "x"),
        executor.submit(slow_2, "y"),
        executor.submit(slow_3, "z"),
    )
    concurrent.futures.wait(futures)
    for future in futures:
        try:
            print(future.result())
        except:  # This should obviously be more explicit
            pass

哪些产出:

2: y
3: z
1: x
1: ok
2: ok
3: ok

我应该注意到,官方文档不清楚您是否可以在将来通过调用.result()从函数中获取返回值,或者您需要循环futures值以获得所述结果.wait()按返回的顺序返回{}和{}值的元组,因此循环done的值对我来说破坏了很多东西。如果你和我一样,只想同时做3件慢的事情,并从这三件事情中得到结果,那么这段代码可能适合你

只有在有东西需要等待的时候,协同程序才能“并行”完成任务。例如,在上面的代码中,使用asyncio.sleep的原因是您可以在上面等待它。您只能等待为此目的而设计的特定功能。这就是为什么标准time.sleep不起作用的原因,因为你不能使用关键字wait。请求库也是如此

幸运的是,您可以使用奇妙的aiohttp库:https://docs.aiohttp.org 这将为您提供同时发出多个请求所需的确切信息

相关问题 更多 >