我正在使用aiohttp实现一个Web API,并使用gunicorn在启用了--worker-class aiohttp.GunicornUVLoopWebWorker
的UVloop的情况下进行部署。因此,我的代码始终在异步上下文中运行。我的想法是在处理请求以提高性能时实施并行作业。
我不使用asyncio
是因为我想要Parallelism,而不是Concurrency。
我知道python中的multiprocessing和GIL problem。但是加入流程也适用于我的问题。
这里是一个例子:
from aiohttp.web import middleware
@middleware
async def context_init(request, handler):
request.context = {}
request.context['threads'] = []
ret = await handler(request)
for thread in request.context['threads']:
thread.join()
return ret
考虑到thread.join()
或process.join()
阻止了当前线程,这将阻止事件循环(据我所知)。如何异步加入?我想要的东西可以用如下图形表示:await thread.join()
或await process.join()
。
更新:
由于@ user4815162342,我能够为我的项目编写适当的代码:
中间件:
from aiohttp.web import middleware
from util.process_session import ProcessSession
@middleware
async def context_init(request, handler):
request.context = {}
request.context['process_session'] = ProcessSession()
request.context['processes'] = {}
ret = await handler(request)
await request.context['process_session'].wait_for_all()
return ret
实用程序:
import asyncio
import concurrent.futures
from functools import partial
class ProcessSession():
def __init__(self):
self.loop = asyncio.get_running_loop()
self.pool = concurrent.futures.ProcessPoolExecutor()
self.futures = []
async def wait_for_all(self):
await asyncio.wait(self.futures)
def add_process(self, f, *args, **kwargs):
ret = self.loop.run_in_executor(self.pool, partial(f, *args, **kwargs))
self.futures.append(ret)
return ret
class ProcessBase():
def __init__(self, process_session, f, *args, **kwargs):
self.future = process_session.add_process(f, *args, **kwargs)
async def wait(self):
await asyncio.wait([self.future])
return self.future.result()
答案 0 :(得分:1)
回答您的问题:是的,它确实阻止了事件循环。
我发现 ThreadPoolExecutor
在这种情况下效果很好。
from util.process_session import ProcessSession
from concurrent.futures.thread import ThreadPoolExecutor
import asyncio
from aiohttp.web import middleware
@middleware
async def context_init(request, handler):
request.context = {}
request.context['threads'] = []
ret = await handler(request)
with ThreadPoolExecutor(1) as executor:
await asyncio.get_event_loop().run_in_executor(executor,
functools.partial(join_threads, data={
'threads': request.context['threads']
}))
return ret
def join_threads(threads):
for t in threads:
t.join()
答案 1 :(得分:0)
我找到了使用multiprocesses的解决方案。可以使用Pool
完成。标准库提供了一些“异步”方法(它不是真正的异步方法,它只是将流程的初始化与流程的输出分开):apply_async
使用一个简单的async wrapper,我设法提供了我想要的东西:
from multiprocessing import Pool
from async_converter import sync_to_async
import asyncio
def f(x):
i = 0
while i < 10000000 * x:
i = i + 1
print("Finished: " + str(x))
return i
async def run():
print("Started with run")
with Pool(processes=4) as pool: # start 4 worker processes
result1 = pool.apply_async(f, (10,)) # evaluate "f(10)" asynchronously
result2 = pool.apply_async(f, (2,))
res1 = await sync_to_async(result1.get)()
print(res1)
res2 = await sync_to_async(result2.get)()
print(res2)
async def dummy(output):
print(output)
async def main():
# Schedule three calls *concurrently*:
await asyncio.gather(
run(),
dummy("Nice"),
dummy("Async"),
dummy("Loop"),
dummy("Perfect"),
dummy("Dummy1"),
dummy("Dummy2"),
dummy("Dummy3"),
dummy("Dummy4"),
dummy("Dummy5"),
dummy("Dummy6"),
dummy("Dummy7"),
dummy("Dummy8"),
dummy("Dummy9"),
dummy("Dummy10"),
)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
输出:
Perfect
Dummy6
Nice
Dummy1
Dummy7
Started with run
Dummy2
Dummy8
Dummy3
Dummy9
Async
Dummy4
Dummy10
Loop
Dummy5
Finished: 2
Finished: 10
100000000
20000000
与asyncio的平行:)