无法将 awaitable 传递给 asyncio.run_coroutine_threadsafe

Chr*_*unt 3 python python-asyncio

我观察到该asyncio.run_coroutine_threadsafe函数不接受一般的可等待对象,我不明白这种限制的原因。观察

import asyncio


async def native_coro():
    return


@asyncio.coroutine
def generator_based_coro():
    return


class Awaitable:
    def __await__(self):
        return asyncio.Future()


loop = asyncio.get_event_loop()

asyncio.run_coroutine_threadsafe(native_coro(), loop)
asyncio.run_coroutine_threadsafe(generator_based_coro(), loop)
asyncio.run_coroutine_threadsafe(Awaitable(), loop)
Run Code Online (Sandbox Code Playgroud)

使用 Python 3.6.6 运行它会产生

Traceback (most recent call last):
  File "awaitable.py", line 24, in <module>
    asyncio.run_coroutine_threadsafe(Awaitable(), loop)
  File "~/.local/python3.6/lib/python3.6/asyncio/tasks.py", line 714, in run_coroutine_threadsafe
    raise TypeError('A coroutine object is required')
TypeError: A coroutine object is required
Run Code Online (Sandbox Code Playgroud)

第 24 行在哪里asyncio.run_coroutine_threadsafe(Awaitable(), loop)

我知道我可以将我的可等待对象包装在一个定义如下的协程中

awaitable = Awaitable()

async def wrapper():
    return await awaitable

asyncio.run_coroutine_threadsafe(wrapper(), loop)
Run Code Online (Sandbox Code Playgroud)

但是我的期望是 awaitable 将是直接到run_coroutine_threadsafe.

我的问题是:

  1. 这个限制的原因是什么?
  2. wrapper上面定义的函数是将awaitable 传递给run_coroutine_threadsafe其他需要async def或生成器定义的协程的API的最传统方式吗?

use*_*342 5

这个限制的原因是什么?

实现来看,原因肯定不是技术上的。由于代码已经调用ensure_future(而不是,比如说,create_task),它会自动工作,并在任何可等待对象上正常工作。

限制的原因可以在跟踪器上找到。作为pull request的结果,该功能是在 2015 年添加的。在有关bpo 问题的讨论中,提交者明确要求将函数重命名为ensure_future_threadsafe(与 并行ensure_future)并接受任何类型的 awaitable,这是 Yury Selivanov 附议的立场。然而,Guido反对这个想法:

我反对这种想法。无论哪种方式,我都没有真正看到这种方法的重要未来:它只是线程和异步世界之间的一点胶水,人们将通过查找示例来学习如何使用它。

[...]

但老实说,我希望鼓励线程和事件循环之间来回翻转; 我认为这是一种必要的罪恶。我们目前拥有的名字来自一个在线程世界中编码的人的 POV,他想将某些东西交给 asyncio 世界。

为什么线程世界中的某个人会有一个他们需要等待的 asyncio.future ?这听起来像是他们混淆了两个世界——或者他们应该编写异步代码而不是线程代码。

还有其他类似的评论,但上面几乎总结了这个论点。

wrapper上面定义的函数是将可等待对象run_coroutine_threadsafe和其他需要异步定义或生成器定义的协程的 API传递的最传统方式吗?

如果您确实需要一个协程对象,那么像这样的东西wrapper肯定是一种直接且正确的获取方式。

如果您创建包装器的唯一原因是调用run_coroutine_threadsafe,但您实际上对结果或concurrent.futures.Future返回的不感兴趣,则run_coroutine_threadsafe可以通过call_soon_threadsafe直接调用来避免包装:

loop.call_soon_threadsafe(asyncio.ensure_future, awaitable)
Run Code Online (Sandbox Code Playgroud)