如何将python asyncio与线程结合起来?

时间:2015-02-13 03:42:01

标签: python multithreading python-asyncio aiohttp

我已经使用Python asyncio和aiohttp成功构建了一个RESTful microservice,它监听POST事件以从各种馈送器收集实时事件。

然后构建一个内存结构来缓存嵌套的defaultdict / deque结构中的最后24h事件。

现在我想定期检查该结构到光盘,最好是使用泡菜。

由于内存结构可能大于100MB,我想避免在检查结构所花费的时间内阻止我的传入事件处理。

我宁愿创建一个结构的快照副本(例如深度复制),然后花时间将其写入磁盘并按预设的时间间隔重复。

我一直在寻找关于如何组合线程的例子(并且这是一个线程甚至是最好的解决方案?)和asyncio用于此目的但找不到能帮助我的东西。

非常感谢任何入门指南!

3 个答案:

答案 0 :(得分:50)

使用BaseEventLoop.run_in_executor将方法委托给线程或子流程非常简单:

import asyncio
import time
from concurrent.futures import ProcessPoolExecutor

def cpu_bound_operation(x):
    time.sleep(x) # This is some operation that is CPU-bound

@asyncio.coroutine
def main():
    # Run cpu_bound_operation in the ProcessPoolExecutor
    # This will make your coroutine block, but won't block
    # the event loop; other coroutines can run in meantime.
    yield from loop.run_in_executor(p, cpu_bound_operation, 5)


loop = asyncio.get_event_loop()
p = ProcessPoolExecutor(2) # Create a ProcessPool with 2 processes
loop.run_until_complete(main())

至于是否使用ProcessPoolExecutorThreadPoolExecutor,这很难说;腌制大型物体肯定会吃掉一些CPU周期,最初你会认为ProcessPoolExecutor是可行的方法。但是,将100MB对象传递给池中的Process需要在主进程中对实例进行pickle,通过IPC将字节发送到子进程,在子进程中取消对其进行取消,然后再次对其进行pickle 所以你可以把它写到磁盘上。考虑到这一点,我的猜测是,即使你因为GIL而遭受性能损失,使用ThreadPoolExecutor最好还是可以使用{{1}}。 / p>

也就是说,测试两种方式并确定无疑是非常简单的,所以你不妨这样做。

答案 1 :(得分:4)

我还使用了<template name="hello"> <ul> {{#each article in articleArray}} <li>{{article.articleObj.name}}</li> {{/each}} </ul> </template> ,但在大多数情况下我发现这个函数有点粗糙,因为它需要run_in_executor用于关键字args而且我从不用一个执行器以外的任何东西调用它和默认的事件循环。所以我用合理的默认值和自动关键字参数处理为它做了一个方便的包装。

partial()

答案 2 :(得分:1)

另一种替代方法是将loop.call_soon_threadsafeasyncio.Queue一起用作沟通的中间渠道。

Python 3的当前文档在Developing with asyncio - Concurrency and Multithreading中也有一节:

import asyncio

# This method represents your blocking code
def blocking(loop, queue):
    import time
    while True:
        loop.call_soon_threadsafe(queue.put_nowait, 'Blocking A')
        time.sleep(2)
        loop.call_soon_threadsafe(queue.put_nowait, 'Blocking B')
        time.sleep(2)

# This method represents your async code
async def nonblocking(queue):
    await asyncio.sleep(1)
    while True:
        queue.put_nowait('Non-blocking A')
        await asyncio.sleep(2)
        queue.put_nowait('Non-blocking B')
        await asyncio.sleep(2)

# The main sets up the queue as the communication channel and synchronizes them
async def main():
    queue = asyncio.Queue()
    loop = asyncio.get_running_loop()

    blocking_fut = loop.run_in_executor(None, blocking, loop, queue)
    nonblocking_task = loop.create_task(nonblocking(queue))

    running = True  # use whatever exit condition
    while running:
        # Get messages from both blocking and non-blocking in parallel
        message = await queue.get()
        # You could send any messages, and do anything you want with them
        print(message)

asyncio.run(main())

如何send asyncio tasks to loop running in other thread也可能会对您有所帮助。