我已经观察到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)
使用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
第24行为asyncio.run_coroutine_threadsafe(Awaitable(), loop)
。
我知道我可以将我的等待对象包装在像这样定义的协程中
awaitable = Awaitable()
async def wrapper():
return await awaitable
asyncio.run_coroutine_threadsafe(wrapper(), loop)
无论如何,我的期望是等待的将直接成为run_coroutine_threadsafe
的有效参数。
我的问题是:
wrapper
函数是否是将等待传递给run_coroutine_threadsafe
以及需要async def
或生成器定义的协程的其他API的最常规方法?答案 0 :(得分:2)
此限制的原因是什么?
从implementation看,原因当然不是技术性的。由于代码已经调用了ensure_future
(而不是create_task
),因此它将在任何等待的对象上自动运行并正确运行。
可以在跟踪器上找到限制的原因。由于pull request,该功能于2015年添加。在有关bpo issue的讨论中,提交者明确要求将函数重命名为ensure_future_threadsafe
(与ensure_future
并行)并接受任何等待的位置,这个位置由Yury Selivanov赞成。但是,Guido的想法是against:
我反对那个主意。无论哪种方式,我都没有真正看到这种方法的重要未来:线程和异步世界之间只有一点点胶水,人们将通过查找示例来学习如何使用它。
[...]
但是说实话,我不要要鼓励在线程和事件循环之间来回切换;我认为这是必不可少的罪恶。我们目前使用的名称是从在线程世界中编码的人想要将某些东西传递给异步世界的POV来确定的。
为什么在线程世界中有人需要等待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)