多年编写多线程代码后,我正在练习async awaitChain() {
try {
const result1 = await startService1();
const result2 = await startService2();
...
const result3 = await startService3();
...
catch (err) {
// handle error
}
// all is well each has been initialized in series
return await bigStart();
}
。
删掉一些我觉得很奇怪的东西。在asyncio
和asyncio
中都有一个concurrent
对象。
Future
猜每个人都有自己的角色。
我的问题是我是否可以将from asyncio import Future
from concurrent.futures import Future
转移到concurrent.future.Future
(或相反)?
答案 0 :(得分:2)
concurrent.futures.Future
是一个对象,我们用来编写基于OS线程或OS进程以及concurrent.futures
模块为我们提供的其他功能的异步代码。
asyncio.Future
是一个对象,我们用来根据协程和asyncio
模块提供的内容编写异步代码。
换句话说,concurrent.futures
和asyncio
尝试解决同一任务,但是方式不同。解决相同的任务意味着在基于线程/进程的方法和基于协程的方法中,许多事情都将是相似的。例如,看看asyncio.Lock和threading.Lock-相似,但不同。
是否可以在不同的类似对象之间进行转换?不,不是。
asyncio
与基于线程的模块之间的本质区别使合作无法实现:
在异步中,您应该await
暂停执行流程并同时执行其他协程。
在基于线程的模块中,通过挂起整个线程来挂起执行流。
例如,当您编写基于线程的代码时,您将编写:
future = concurrent.futures.Future()
# ...
result = future.result() # acts like time.sleep blocking whole thread
但是在异步中,您不应该阻塞线程,应该将控制权返回给事件循环:
future = asyncio.Future()
# ...
result = await future # block current execution flow returning control to event loop
# without blocking thread,
# current flow will be resumed later
答案 1 :(得分:2)
我的问题是我是否可以将
concurrent.future.Future
转移到asyncio.Future
(或相反)?
如果“转移”的意思是“将一个转换为另一个”,是可以的,尽管桥接方法之间的阻抗不匹配可能需要一些工作。
要将concurrent.futures.Future
转换为asyncio.Future
,可以调用asyncio.wrap_future
。返回的asyncio将来可在asyncio事件循环中等待,并且将在基础线程的future完成时完成。有效地实现了run_in_executor
。
没有公共功能可以将asyncio的未来直接转换为concurrent.futures
的未来,但是有asyncio.run_coroutine_threadsafe
函数需要一个协程并将其提交给事件循环,并返回并发的Future,该异步的Future在异步Future完成时完成。这可以用来有效地将任何异步等待的将来转换为并发的将来,例如:
def to_concurrent(fut, loop):
async def wait():
await fut
return asyncio.run_coroutine_threadsafe(wait(), loop)
返回的未来的行为将与您期望的并发未来的行为类似,例如其result()
方法将阻塞,等等。您可能要注意的一件事是,以add_done_callback
运行的回调(一如既往)在完成未来的线程中运行,在这种情况下,事件循环线程。这意味着,如果添加完成的回调,则需要注意不要在其实现中调用阻塞调用,以免阻塞事件循环。
答案 2 :(得分:1)
对于“并发未来到异步未来”部分,这是我使用的实用程序。
from typing import List, Any
from concurrent.futures.thread import ThreadPoolExecutor
import asyncio
class AsyncThreadPool(ThreadPoolExecutor):
_futures: List[asyncio.Future]
_loop: asyncio.AbstractEventLoop
def __init__(self, max_workers=None):
super().__init__(max_workers)
self._futures = []
def queue(self, fn):
self._loop = asyncio.get_event_loop()
fut = self._loop.create_future()
self._futures.append(fut)
self.submit(self._entry, fn, fut)
def queueAsync(self, coroutine):
def newLoop():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(coroutine)
self.queue(newLoop)
def _entry(self, fn, fut: asyncio.Future):
try:
result = fn()
self._loop.call_soon_threadsafe(fut.set_result, result)
except Exception as e:
self._loop.call_soon_threadsafe(fut.set_exception, e)
async def gather(self) -> List[Any]:
return await asyncio.gather(*self._futures)
您可以这样使用它:
with AsyncThreadPool() as pool:
# Queue some sync function (will be executed on another thread)
pool.queue(someHeavySyncFunction)
# Queue a coroutine that will be executed on a new event loop running on another thread
pool.queue(otherAsyncFunction())
# Gather results (non blocking for your current loop)
res: List[Any] = await pool.gather()
答案 3 :(得分:1)
异步中有一个名为wrap_future
的函数。
在一个asyncio.Future对象中包装一个current.futures.Future对象。
请参见https://docs.python.org/3/library/asyncio-future.html#asyncio.wrap_future