[英]Non-blocking launching of concurrent coroutines in Python
我想异步并发地执行任务。 如果task1
时运行task2
到达, task2
是马上开始,而无需等待task2
完成。 另外,我想避免在协程的帮助下进行回调。
这是带有回调的并发解决方案:
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
class FibonacciCalculatorFuture:
def __init__(self):
self.pool = ThreadPoolExecutor(max_workers=2)
@staticmethod
def calculate(n):
print(f"started n={n}")
return fibonacci(n)
def run(self, n):
future = self.pool.submit(self.calculate, n)
future.add_done_callback(lambda f: print(f.result()))
if __name__ == '__main__':
calculator = FibonacciCalculatorFuture()
calculator.run(35)
calculator.run(32)
print("initial thread can continue its work")
其输出:
started n=35
started n=32
initial thread can continue its work
3524578
14930352
这是我摆脱回调的努力:
class FibonacciCalculatorAsync:
def __init__(self):
self.pool = ThreadPoolExecutor(max_workers=2)
self.loop = asyncio.get_event_loop()
@staticmethod
def calculate_sync(n):
print(f"started n={n}")
return fibonacci(n)
async def calculate(self, n):
result = await self.loop.run_in_executor(self.pool, self.calculate_sync, n)
print(result)
def run(self, n):
asyncio.ensure_future(self.calculate(n))
if __name__ == '__main__':
calculator = FibonacciCalculatorAsync()
calculator.run(35)
calculator.run(32)
calculator.loop.run_forever()
print("initial thread can continue its work")
输出:
started n=35
started n=32
3524578
14930352
在这种情况下,初始线程将无法比loop.run_forever()
,因此将无法接受新任务。
所以,这是我的问题 :是否可以同时进行:
即使内部没有任何任务, loop.run_forever()
的确会永远运行。 好消息是您不需要此功能。 为了等待您的计算完成,请使用asyncio.gather
:
class FibonacciCalculatorAsync:
def __init__(self):
self.pool = ThreadPoolExecutor(max_workers=2)
# self.loop = asyncio.get_event_loop()
...
async def calculate(self, n):
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(self.pool, self.calculate_sync, n)
print(result)
async def main():
calculator = FibonacciCalculatorAsync()
fib_35 = asyncio.ensure_future(calculator.run(35))
fib_32 = asyncio.ensure_future(calculator.run(32))
print("initial thread can continue its work")
...
# demand fibonaccy computation has ended
await asyncio.gather(fib_35, fib_32)
if __name__ == '__main__':
asyncio.run(main())
请注意这里如何处理循环-我做了几件事。 如果您开始使用asyncio,我实际上建议对所有内容使用一个循环,而不是为更细化的任务创建循环。 通过这种方法,你得到所有ASYNCIO 花里胡哨的处理和同步任务。
同样,由于GIL,不可能在ThreadPoolExecutor
并行化纯Python非IO代码。 请记住这一点,在这种情况下,最好使用进程池执行程序。
您可以通过在专用线程中运行asyncio并使用asyncio.run_coroutine_threadsafe
安排协程来满足您问题的第二个asyncio.run_coroutine_threadsafe
。 例如:
class FibonacciCalculatorAsync:
def __init__(self):
self.pool = ThreadPoolExecutor(max_workers=2)
self.loop = asyncio.get_event_loop()
@staticmethod
def calculate_sync(n):
print(f"started n={n}")
return fibonacci(n)
async def calculate(self, n):
result = await self.loop.run_in_executor(self.pool, self.calculate_sync, n)
print(result)
def run(self, n):
asyncio.run_coroutine_threadsafe(self.calculate(n), self.loop)
def start_loop(self):
thr = threading.Thread(target=self.loop.run_forever)
thr.daemon = True
thr.start()
if __name__ == '__main__':
calculator = FibonacciCalculatorAsync()
calculator.start_loop()
calculator.run(35)
calculator.run(32)
print("initial thread can continue its work")
calculator.run(10)
time.sleep(1)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.