为啥 asyncio.Future 与 concurrent.futures.Future 不兼容?

Posted

技术标签:

【中文标题】为啥 asyncio.Future 与 concurrent.futures.Future 不兼容?【英文标题】:Why is asyncio.Future incompatible with concurrent.futures.Future?为什么 asyncio.Future 与 concurrent.futures.Future 不兼容? 【发布时间】:2017-10-08 12:02:23 【问题描述】:

这两个类代表了并发编程的优秀抽象,因此它们不支持相同的 API 有点令人不安。

具体根据docs:

asyncio.Future 几乎与concurrent.futures.Future 兼容。

区别:

result()exception() 不接受超时参数,并在未来尚未完成时引发异常。 使用add_done_callback() 注册的回调始终通过事件循环的call_soon_threadsafe() 调用。 此类与concurrent.futures 包中的wait()as_completed() 函数不兼容。

上面的列表实际上是不完整的,还有一些不同之处:

running() 方法不存在 如果过早调用result()exception() 可能会提高InvalidStateError

这些是否是由于事件循环的固有特性导致这些操作无用或太麻烦而无法实现?

add_done_callback()相关的差异是什么意思?无论哪种方式,回调都保证在期货完成后的某个未指定时间发生,那么这两个类之间不是完全一致吗?

【问题讨论】:

【参考方案1】:

差异的核心原因在于线程(和进程)如何处理阻塞与协程如何处理阻塞事件。在线程中,当前线程被挂起,直到任何条件解决并且线程可以继续前进。例如,在期货的情况下,如果您请求未来的结果,则可以暂停当前线程,直到该结果可用。

但是,事件循环的并发模型是,您不会暂停代码,而是返回到事件循环并在准备好时再次被调用。因此,请求未准备好结果的 asyncio 未来的结果是错误的。

您可能会认为 asyncio 未来可以等待,虽然这会效率低下,但您的协程阻塞真的有那么糟糕吗?事实证明,拥有协程块很可能意味着未来永远不会完成。未来的结果很可能由与运行请求结果的代码的事件循环相关联的一些代码设置。如果运行该事件循环的线程阻塞,则不会运行与该事件循环关联的代码。所以阻塞结果会死锁并阻止结果产生。

所以,是的,界面的差异是由于这种固有的差异。例如,您不希望将 asyncio 未来与 concurrent.futures 服务员抽象一起使用,因为这又会阻塞事件循环线程。

add_done_callbacks 的区别保证回调将在事件循环中运行。这是可取的,因为他们将获得事件循环的线程本地数据。此外,许多协程代码假定它永远不会与来自同一事件循环的其他代码同时运行。也就是说,协程只有在来自同一个事件循环的两个协程不同时运行的假设下才是线程安全的。在事件循环中运行回调可以避免很多线程安全问题,并且更容易编写正确的代码。

【讨论】:

【参考方案2】:

concurrent.futures.Future 提供了一种在不同线程和进程之间共享结果的方法,通常在您使用Executor 时。

asyncio.Future 解决了相同的任务,但对于coroutines,这实际上是一些特殊类型的函数,通常在一个进程/线程中异步运行。当前上下文中的“异步”意味着事件循环管理此协程的代码执行流程:它可能会在一个协程内暂停执行,开始执行另一个协程,然后返回执行第一个协程 - 通常一切都在一个线程/进程中。

这些对象(以及许多其他 threading/asyncio 对象,如 LockEventSemaphore 等)看起来很相似,因为您的代码中线程/进程和协程的并发概念是相似的.

我认为对象不同的主要原因是历史原因:asyncio 的创建要晚得多,然后 threadingconcurrent.futures。在不破坏类 API 的情况下,可能无法将 concurrent.futures.Future 更改为与 asyncio 一起使用。

在“理想世界”中这两个类都应该是一个吗?这可能是一个值得商榷的问题,但我看到了它的许多缺点:虽然asynciothreading 乍一看很相似,但它们在很多方面都非常不同,包括内部实现或编写异步/非异步代码的方式(见async/await关键字)。

我认为类的不同可能是最好的:我们显然在本质上划分了不同的并发方式(即使它们的相似性起初看起来很奇怪)。

【讨论】:

以上是关于为啥 asyncio.Future 与 concurrent.futures.Future 不兼容?的主要内容,如果未能解决你的问题,请参考以下文章

Python asyncio ensure_future 装饰器

Python TypeError:“_asyncio.Future”对象不可下标

TypeError:“_asyncio.Future”对象不可下标,使用 mongodb 的异步电机驱动程序

python 期物

将 asyncio.ensure_future 和 loop.run_until_complete 组合在一个 api 调用中?

如何获取协程的返回值