为什么我的异步函数同步运行Python3.9?

为什么我的异步函数同步运行Python3.9?,python,python-3.x,python-asyncio,python-multithreading,concurrent.futures,Python,Python 3.x,Python Asyncio,Python Multithreading,Concurrent.futures,我正在尝试使用asyncio和futures在不同的线程上运行函数。我有一个decorator,它异步获取长时间运行的函数及其参数,并输出其值。不幸的是,这些进程似乎不是异步工作的 def multiprocess(self, function, executor=None, *args, **kwargs): async def run_task(function, *args, **kwargs): @functools.wraps(function)

我正在尝试使用
asyncio
futures
在不同的线程上运行函数。我有一个decorator,它异步获取长时间运行的函数及其参数,并输出其值。不幸的是,这些进程似乎不是异步工作的

def multiprocess(self, function, executor=None, *args, **kwargs):
    async def run_task(function, *args, **kwargs):
        @functools.wraps(function)
        async def wrap(*args, **kwargs):
            while True:
                execution_runner = executor or self._DEFAULT_POOL_
                executed_job = execution_runner.submit(function, *args, **kwargs)
                print(
                    f"Pending {function.__name__}:",
                    execution_runner._work_queue.qsize(),
                    "jobs",
                )
                print(
                    f"Threads: {function.__name__}:", len(execution_runner._threads)
                )
                future = await asyncio.wrap_future(executed_job)
                return future

        return wrap

    return asyncio.run(run_task(function, *args, **kwargs))
要调用decorator,我有两个函数
\u async\u task
task\u function
\u async\u task
包含一个循环,该循环为需要处理的每个文档运行
task\u函数

@staticmethod
def _async_task(documents):
    processed_docs = asyncio.run(task_function(documents))
    return processed_docs
task_函数
处理文档中的每个文档,如下所示:

@multiprocess
async def task_function(documents):
    processed_documents = None
    try:
        for doc in documents:
            processed_documents = process_document(doc)
            print(processed_documents)
    except Exception as err:
        print(err)
    return processed_documents
这不能异步工作的线索是,我对多线程装饰器的诊断打印了以下内容

Pending summarise_news: 0 jobs
Threads: summarise_news: 2

由于没有挂起的作业,而且整个过程与同步运行的时间一样长,因此它是同步运行的。

我在设置代码时遇到了一些问题,但我想我已经找到了答案

def multiprocess(self, function, executor=None, *args, **kwargs):
    async def run_task(function, *args, **kwargs):
        @functools.wraps(function)
        async def wrap(*args, **kwargs):
            while True:
                execution_runner = executor or self._DEFAULT_POOL_
                executed_job = execution_runner.submit(function, *args, **kwargs)
                print(
                    f"Pending {function.__name__}:",
                    execution_runner._work_queue.qsize(),
                    "jobs",
                )
                print(
                    f"Threads: {function.__name__}:", len(execution_runner._threads)
                )
                future = await asyncio.wrap_future(executed_job)
                return future

        return wrap

    return asyncio.run(run_task(function, *args, **kwargs))
首先,正如@dano在他的评论中提到的,
asyncio.run
阻塞,直到协同程序运行完成。因此,使用这种方法不会得到任何加速

我使用了一个稍加修改的
多进程
装饰器

def多进程(executor=None,*args,**kwargs):
def run_任务(函数,*args,**kwargs):
def包裹(*args,**kwargs):
execution\u runner=执行器或默认执行器
executed_job=execute_runner.submit(函数,*args,**kwargs)
印刷品(
f“挂起的{函数名:”,
执行\u runner.\u work\u queue.qsize(),
“工作”,
)
印刷品(
f“Threads:{function.\uuuuu name\uuuuuu}:”,len(执行线程)
)
future=asyncio.wrap\u future(已执行的作业)
回归未来
回程包装
返回运行任务
正如您所看到的,这里没有
asyncio.run
,装饰器和内部包装器都是同步的,因为
asyncio.wrap\u future
不需要
wait

更新的
多进程
装饰器现在与
进程文档
功能一起使用。原因是,并行化一个按顺序调用阻塞函数的函数不会带来任何好处。您必须将阻塞函数转换为可在执行器中运行

注意这个虚拟的
进程\u文档
与我描述的完全一样-完全阻塞和同步

@multiprocess()
def流程文件(doc):
打印(f“处理文档:{doc}…”)
时间。睡眠(2)
打印(f“Doc{Doc}done.”)
现在,说到最后一点。我们已经将
process\u文档
转换为可在执行器中运行,从而使其具有某种异步性,但如何准确地调用它仍然很重要

考虑以下示例:

对于文档中的文档:
结果=等待处理文件(doc)
results=wait asyncio.gather(*[为文档中的文档处理文档(doc)])
在前一种情况下,我们将按顺序等待协同路由,必须等到一个完成后再开始另一个。 在后一个示例中,它们将并行执行,因此这实际上取决于调用协同程序执行的方式

以下是我使用的完整代码:

导入异步IO
进口期货
导入时间
默认的线程执行器=concurrent.futures.ThreadPoolExecutor(最大线程数=4)
def多进程(执行器=无,*args,**kwargs):
def run_任务(函数,*args,**kwargs):
def包裹(*args,**kwargs):
execution\u runner=执行器或默认执行器
executed_job=execute_runner.submit(函数,*args,**kwargs)
印刷品(
f“挂起的{函数名:”,
执行\u runner.\u work\u queue.qsize(),
“工作”,
)
印刷品(
f“Threads:{function.\uuuuu name\uuuuuu}:”,len(执行线程)
)
future=asyncio.wrap\u future(已执行的作业)
回归未来
回程包装
返回运行任务
@多进程()
def流程文件(doc):
打印(f“处理文档:{doc}…”)
时间。睡眠(2)
打印(f“Doc{Doc}done.”)
异步def任务\功能\顺序(文档):
开始=时间。时间()
对于文档中的文档:
等待处理文件(doc)
end=time.time()
打印(f“任务功能:{end start}s”)
异步def任务\功能\并行(文档):
开始=时间。时间()
作业=[处理文档中文档的文档(文档)]
等待asyncio.gather(*作业)
end=time.time()
打印(f“任务函数并行执行:{end start}s”)
异步def main():
文件=[i代表范围(5)内的i]
等待任务\功能\顺序(文档)
等待任务\功能\并行(文档)
asyncio.run(main())

请注意,
task\u function\u parallel
示例仍然需要大约4秒,而不是2秒,因为线程池限制为4个工作线程,并且作业数为5个,因此最后一个作业将等待一些工作线程可用。

具体来说,您希望在这里异步运行什么
\u async\u task
只需调用
task\u函数一次<代码>任务\函数
在工作线程池中运行。但是您使用的是
asyncio.run
\u async\u task
中,这意味着它将一直阻塞,直到
task\u函数完成。任何后续的
\u async\u任务
调用都不会运行,直到前一个调用完成为止。@dano查看下面的答案。我似乎误解了异步在python中的工作方式。从本质上说,我希望任务函数能够异步工作,并将所有异步运行的输出收集到数据结构中,这样下游的同步函数就可以处理它了。这