为什么我的异步函数同步运行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中的工作方式。从本质上说,我希望任务函数能够异步工作,并将所有异步运行的输出收集到数据结构中,这样下游的同步函数就可以处理它了。这