Не удается передать ожидаемое 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,

Мои вопросы:

  1. В чем причина этого ограничения?
  2. Это 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)
Другие вопросы по тегам