await, похоже, блокируется в asycio.Будущее

#python-3.x #async-await #future

#python #python-3.x #async-await #python-asyncio #будущее

Вопрос:

Я пытаюсь понять python async / await и использую Future объекты, чтобы указать, что функция может продолжаться. Это некоторый код, который воспроизводит проблему, с которой я столкнулся:

 import time, threading, asyncio

loop = asyncio.get_event_loop()
f = loop.create_future()

def resolve(fut):
    for i in range(3):
        print(i)
        time.sleep(1)
    fut.set_result(88)

async def wait_on_future(fut):
    print('waiting for fut')
    await fut
    print('done', fut.result())
    return fut.result()

threading.Thread(target=resolve, args=(f,)).start()
loop.create_task(wait_on_future(f))
loop.run_forever()
  

который печатает:

 0
waiting for fut
1
2
  

Обратите внимание, что он никогда не печатается 'done' . В разделе ожидаемых документов говорится:

Когда ожидается объект Future, это означает, что сопрограмма будет ждать, пока будущее не будет разрешено в каком-либо другом месте.

Я думал, что вызов set_result — это способ разрешить a Future . Чего мне здесь не хватает?

Примечание: если я вызываю resolve в том же потоке, это работает нормально. Реальная проблема, которую я пытаюсь решить, заключается в том, что событие, которое происходит для разрешения Future , находится в потоке. Я заметил, что в будущих документах говорится, что он не является потокобезопасным. Как создать асинхронную функцию, которая ожидает события, происходящего в другом потоке?

Комментарии:

1. «> Я заметил, что в будущих документах говорится, что это не потокобезопасно.» Большая часть asyncio не является потокобезопасной. Часто не имеет смысла использовать как asyncio, так и потоки. Вы пробовали вместо этого использовать многопроцессорную обработку?

2. @CharmingRobot Потоки происходят из кода C , обернутого cython. Я пытаюсь использовать обратные вызовы, запускаемые этими потоками, для разрешения фьючерсов в некотором асинхронном коде. Я знаком с многопроцессорной обработкой; это не помогает решить эту проблему, но спасибо за мысль.

3. Вы взглянули на функцию run_in_executor asyncio? может быть, вы можете вызвать `await run_in_executor(resolve (f)) из своей сопрограммы?

4. Также, если вы хотите запланировать сопрограмму в другом потоке, вы можете использовать asyncio.run_coroutine_threadsafe . docs.python.org/3/library /…

5. @CharmingRobot Я думаю run_coroutine_threadsafe , может сработать для меня, спасибо за ссылку!

Ответ №1:

Хорошо, благодаря комментарию @CharmingRobot о run_coroutine_threadsafe том, что я пришел к этому решению:

 import time, threading, asyncio

loop = asyncio.get_event_loop()
f = loop.create_future()

def resolve(fut):
    for i in range(3):
        print(i)
        time.sleep(1)
    async def a_resolve():
        fut.set_result(88)
    asyncio.run_coroutine_threadsafe(a_resolve(), loop)

async def wait_on_future(fut):
    print('waiting for fut')
    return await fut

async def print_future():
    print('got the future! value:', await wait_on_future(f))

loop.create_task(print_future())
threading.Thread(target=resolve, args=(f,)).start()
loop.run_forever()
  

который печатает:

 0
waiting for fut
1
2
got the future! value: 88
  

это то, что я искал.