调用thread.join()是否会在异步上下文中阻止事件循环?

时间:2018-11-02 18:24:37

标签: python multithreading python-asyncio

我正在使用aiohttp实现一个Web API,并使用gunicorn在启用了--worker-class aiohttp.GunicornUVLoopWebWorker的UVloop的情况下进行部署。因此,我的代码始终在异步上下文中运行。我的想法是在处理请求以提高性能时实施并行作业。

我不使用asyncio是因为我想要Parallelism,而不是Concurrency

我知道python中的multiprocessingGIL 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()

2 个答案:

答案 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的平行:)