Не удается передать ожидаемое asyncio.run_coroutine_threadsafe
Я заметил, что 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
и другие API, которые требуютasync def
или генераторная сопрограмма?
1 ответ
В чем причина этого ограничения?
Глядя на реализацию, причина, конечно, не техническая. Поскольку код уже вызывает ensure_future
(а не, скажем, create_task
), он будет автоматически работать и работать правильно на любом ожидаемом объекте.
Причину ограничения можно найти на трекере. Функция была добавлена в 2015 году по запросу. В обсуждении связанной проблемы bpo отправитель явно просит переименовать функцию в ensure_future_threadsafe
(параллельно ensure_future
) и принять любую приемлемую позицию, поддержанную Юрием Селивановым. Однако Гвидо был против идеи:
Я против этой идеи. В любом случае я не вижу большого важного будущего для этого метода: это всего лишь немного клея между поточным миром и асинхронным миром, и люди узнают, как его использовать, найдя пример.
[...]
Но, честно говоря, я не хочу поощрять переключаться между потоками и циклами событий; Я вижу это как необходимое зло. Имя, которое у нас есть в настоящее время, отлично от POV кого-то, кто кодирует в многопоточном мире и хочет передать что-то миру асинцио.
Зачем кому-то в многопоточном мире иметь асинхронное будущее, которого ему нужно ждать? Похоже, что они смешивают два мира - или они должны писать асинхронный код вместо многопоточного кода.
Есть и другие комментарии в том же духе, но вышесказанное в значительной степени суммирует аргумент.
Это
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)