当前位置: 首页 > 知识库问答 >
问题:

如何使用python的asyncio模块正确地创建和运行并发任务?

方兴旺
2023-03-14

我试图正确理解和实现两个并发运行的任务对象使用Python 3相对较新的异步模块。

简而言之,asyncio似乎被设计用于处理异步进程和事件循环上的并发Task执行。它提倡使用wait(应用于异步函数)作为无回调的方式来等待和使用结果,而不阻塞事件循环。(期货和回调仍然是可行的选择。)

它还提供了asyncio。任务()类,一个专门的子类,用于包装协程。最好通过使用asyncio.ensure_future()方法调用。异步任务的预期用途是允许独立运行的任务与同一事件循环中的其他任务“并发”运行。我的理解是,任务连接到事件循环,然后事件循环自动驱动wait语句之间的协程。

我喜欢能够使用并发任务而不需要使用Executor类的想法,但我还没有发现太多关于实现的细节。

我现在就是这样做的:

import asyncio

print('running async test')

async def say_boo():
    i = 0
    while True:
        await asyncio.sleep(0)
        print('...boo {0}'.format(i))
        i += 1

async def say_baa():
    i = 0
    while True:
        await asyncio.sleep(0)
        print('...baa {0}'.format(i))
        i += 1

# wrap in Task object
# -> automatically attaches to event loop and executes
boo = asyncio.ensure_future(say_boo())
baa = asyncio.ensure_future(say_baa())

loop = asyncio.get_event_loop()
loop.run_forever()

在试图同时运行两个循环任务的情况下,我注意到,除非任务有一个内部的wait表达式,否则它将卡在循环中,有效地阻止其他任务运行(很像正常的>而循环)。然而,一旦任务必须等待,它们似乎就会并发运行,没有问题。

因此,wait语句似乎为事件循环提供了在任务之间来回切换的立足点,从而产生了并发效果。

示例输出与内部等待

running async test
...boo 0
...baa 0
...boo 1
...baa 1
...boo 2
...baa 2

没有内部等待的输出示例:

...boo 0
...boo 1
...boo 2
...boo 3
...boo 4

这个实现是否传递了asyncio中并发循环任务的适当示例?

唯一有效的方法是任务提供阻塞点(await表达式),以便事件循环处理多个任务,这是否正确?


共有2个答案

霍财
2023-03-14

您不一定需要从x获得收益来控制事件循环。

在您的示例中,我认为正确的方法是执行一个收益无,或者等效地执行一个简单的收益,而不是从asyncio.sleep(0.001)生成一个

import asyncio

@asyncio.coroutine
def say_boo():
  i = 0
  while True:
    yield None
    print("...boo {0}".format(i))
    i += 1

@asyncio.coroutine
def say_baa():
  i = 0
  while True:
    yield
    print("...baa {0}".format(i))
    i += 1

boo_task = asyncio.async(say_boo())
baa_task = asyncio.async(say_baa())

loop = asyncio.get_event_loop()
loop.run_forever()

协同程序只是普通的老Python生成器。在内部,asyncio事件循环保存这些生成器的记录,并在一个永无止境的循环中逐个调用gen.send()。无论何时产生,对gen.send()的调用都将完成,循环可以继续。(我正在简化它;看看周围https://hg.python.org/cpython/file/3.4/Lib/asyncio/tasks.py#l265 (对于实际代码)

也就是说,如果你需要在不共享数据的情况下进行CPU密集型计算,我仍然会走run_in_executor路线。

丰超
2023-03-14

是的,在事件循环中运行的任何协同程序都会阻止其他协同程序和任务运行,除非

  1. 使用wait调用另一个协程(如果使用Python 3.5)。
  2. 返回

这是因为asyncio是单线程的;事件循环运行的唯一方法是不让其他协同程序积极执行。使用yield from/await暂时挂起协同程序,使事件循环有机会工作。

您的示例代码很好,但是在许多情况下,您可能不希望长时间运行的代码在事件循环中不执行异步I/O。在这种情况下,使用asyncio.loop.run_in_executor在后台线程或进程中运行代码通常更有意义。如果您需要执行一些对asyncio不友好的I/O,则会使用。

例如,您的两个循环完全受CPU限制,不共享任何状态,因此最好的性能来自于使用ProcessPoolExecutor跨CPU并行运行每个循环:

import asyncio
from concurrent.futures import ProcessPoolExecutor

print('running async test')

def say_boo():
    i = 0
    while True:
        print('...boo {0}'.format(i))
        i += 1


def say_baa():
    i = 0
    while True:
        print('...baa {0}'.format(i))
        i += 1

if __name__ == "__main__":
    executor = ProcessPoolExecutor(2)
    loop = asyncio.get_event_loop()
    boo = loop.run_in_executor(executor, say_boo)
    baa = loop.run_in_executor(executor, say_baa)

    loop.run_forever()
 类似资料:
  • 问题内容: 我正在尝试使用Python 3的相对较新的模块正确理解和实现两个同时运行的对象。 简而言之,asyncio似乎旨在处理事件循环中的异步过程和并发执行。它促进了(应用于异步函数中)作为无回调方式等待和使用结果的使用,而不会阻塞事件循环。(期货和回调仍然是可行的选择。) 它还提供了该类,该类 是用于包装协程的专门子类。最好通过使用该方法来调用。异步任务的预期用途是允许独立运行的任务与同一事

  • 问题内容: 我需要创建一个可以同时从Web套接字和管道接收消息的软件,并在另一个通道上发送消息(它从套接字接收消息,创建一个新线程并发送到管道。以与从管道接收消息相同的方式,创建一个新线程并发送到套接字。 我在使用多线程时遇到问题,在程序启动时,我必须启动方法,但只能启动。我尝试删除所有代码并仅保留,但仅输入的。 该程序由子进程调用,父进程通过连接到stdout和stdin的管道与之通信。 更新:

  • 这就是我所拥有的理想构建脚本: 我确实想手动执行任务“unzip_natives_os”。但它似乎只在配置阶段起作用。当我用这个设置进行测试运行时,它会给我一个错误:“java.lang.UnsatifiedLinkError”,但是如果我在dependencies块中将配置从“NativeSos”更改为“RuntimeOnly”,它就能正常工作。我是否必须显式地创建这个“ApplicationD

  • 问题内容: 假设我们有很多链接可供下载,并且每个链接可能花费不同的时间来下载。而且我只能使用最多3个连接进行下载。现在,我想确保使用asyncio有效地做到这一点。 这是我要实现的目标:在任何时间点,请尝试确保至少运行3个下载。 数字代表下载链接,连字符代表等待下载。 这是我现在正在使用的代码 输出是预期的: 但是这是我的问题: 目前,我只是在等待9秒钟以使主要功能保持运行状态,直到下载完成。在退

  • 4.4.1 模块的创建和使用 在 Python 语言中,模块对应于 Python 程序文件,即每个 Python 程序文件就是一个模块。 模块是 Python 程序的最高层结构单元,用于组织程序的代码和数据,以便能被同一程 序的其他模块甚至被其他程序重用。一个模块可以导入其他模块,导入后就可以使用其他模 块中定义的函数、类等对象。 用模块作为程序的结构单元,至少有三个作用: (1)代码重用:将代码

  • 问题内容: 我正在尝试使用Bokeh而不是matplotlib复制此问题中显示的HeatMap。我不能完全正确。现有的示例并没有帮助我了解我在做什么错。我卑微的尝试 给 请注意,尽管标题相似,但这并不能回答我的问题。这不是相同的错误,我正在使用Bokeh 0.12.3 问题答案: 更改数据的生成以按元素重复,并且它应该是正确的: 因此,适用于我的代码如下: