可以将current.futures.Future转换为asyncio.Future吗?

时间:2019-01-08 16:52:17

标签: python-3.x python-asyncio python-multithreading concurrent.futures

多年编写多线程代码后,我正在练习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(); }

删掉一些我觉得很奇怪的东西。在asyncioasyncio中都有一个concurrent对象。

Future

猜每个人都有自己的角色。

我的问题是我是否可以将from asyncio import Future from concurrent.futures import Future 转移到concurrent.future.Future(或相反)?

4 个答案:

答案 0 :(得分:2)

concurrent.futures.Future是一个对象,我们用来编写基于OS线程或OS进程以及concurrent.futures模块为我们提供的其他功能的异步代码。

asyncio.Future是一个对象,我们用来根据协程和asyncio模块提供的内容编写异步代码。

换句话说,concurrent.futuresasyncio尝试解决同一任务,但是方式不同。解决相同的任务意味着在基于线程/进程的方法和基于协程的方法中,许多事情都将是相似的。例如,看看asyncio.Lockthreading.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