在两个不同的线程中运行asyncio以即时处理操作

时间:2015-07-24 14:41:36

标签: python python-asyncio

我正在尝试异步处理许多操作:我想将操作发送到我的循环并同时在ProcessPoolExecutor中运行它们。我想我不知道我将在开始时运行的所有工作,所以我无法定义所有工作,然后启动事件循环。

我找到的唯一解决方案是运行一个可以处理动作的主线程,以及另一个执行loop.run_forever的线程,这似乎有效。但是,我没有看到以这种方式在同一个循环上运行的两个单独线程的任何示例。有没有其他方法可以解决这个问题,如果我的解决方案有什么问题?

import asyncio
from concurrent.futures import ProcessPoolExecutor
import functools
import time
import threading


executor = ProcessPoolExecutor(max_workers=3)


def do_work(eventloop, value):
    future = eventloop.run_in_executor(executor, functools.partial(process_action, value))
    future.add_done_callback(run_job_success)


def process_action(value):
    print("Processing %i" % value)
    time.sleep(1)
    return value


def run_job_success(f):
    print("Success : %s" % f.result())


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop_thread = threading.Thread(target=loop.run_forever)
    loop_thread.start()
    while True:
        msg = recv()
        if msg is not None:
            do_work(loop, msg)

修改 我使用recv方法获得了工作。

1 个答案:

答案 0 :(得分:1)

你要做的事情有点含糊不清 - 你说你不知道你想在程序开始时运行的所有工作。这没关系 - 但 如何找到你想要运行的工作?在任何情况下,通过使用BaseEventLoop.call_soon在开始事件循环之前安排您要进行的所有do_work调用,上面的测试程序可以(并且应该)重写为单线程:

import asyncio
from concurrent.futures import ProcessPoolExecutor
import functools
import time

def do_work(eventloop, value):
    future = eventloop.run_in_executor(executor, functools.partial(process_action, value))
    future.add_done_callback(run_job_success)

def process_action(value):
    print("Processing %i" % value)
    time.sleep(1)
    return value

def run_job_success(f):
    print("Success : %s" % f.result())

if __name__ == "__main__":
    executor = ProcessPoolExecutor(max_workers=3)
    loop = asyncio.get_event_loop()
    for i in range(5):
        loop.call_soon(do_work, loop, i)
    loop.run_forever()

或者可以使用协同程序而不是回调来进一步重构,这通常是使用asyncio时的首选样式:

import time
import asyncio
import functools
from concurrent.futures import ProcessPoolExecutor

def do_work(loop, value):
    return loop.run_in_executor(executor, functools.partial(process_action, value))

def process_action(value):
    print("Processing %i" % value)
    time.sleep(1)
    return value

@asyncio.coroutine
def main(loop):
    tasks = [do_work(loop, i) for i in range(5)]
    for fut in asyncio.as_completed(tasks):
        result = yield from fut
        print("Success : %s" % result)

if __name__ == "__main__":
    executor = ProcessPoolExecutor(max_workers=3)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))

这也使得在完成所有工作后更容易退出程序,而不必使用Ctrl + C和loop.run_forever

您当前的方法是安全的(loop.run_in_executor使用loop.call_soon_threadsafe封面,这是您可以安全/正确地安排来自单独线程的事件循环中工作的唯一方法),它只是过度 - 复杂而不必要; asyncio的设计使得使用它的程序是单线程的(除非需要运行阻塞操作,而run_in_executor就是这样)。