Статус будущего внутри цикла всегда в ожидании

1

Прежде всего, я запускаю это на ноутбуке Jupyter, и это может быть причиной моего замешательства.

У меня есть функция foo которая выполняет некоторые операции ввода-вывода:

def foo():
    # Dom some stuff
    time.sleep(1.)
    return 'Finished'

Я хочу иметь возможность запускать эту функцию в фоновом режиме, поэтому я решил использовать run_in_executor:

future = asyncio.get_event_loop().run_in_executor(None, foo)

Теперь операция неблокирующая, и все нормально, но если это будущее возвращает ошибку, я хочу перехватить ее и остановить основную программу, поэтому я решил написать цикл, который продолжает проверять состояние этого будущего:

while True:
    time.sleep(0.1)
    if future.done():
    # Do some stuff, like raising any caught exception

Проблема в том, что будущий статус никогда не изменяется, внутри цикла он всегда ожидает.

Если вместо проверки в цикле я вручную проверю состояние будущего (в блокноте jupyter), оно будет правильно помечено как завершенное. Такое поведение смущает меня...

Как я могу продолжать проверять статус будущего внутри формулировки цикла?

Теги:
multithreading
python-asyncio
future

1 ответ

1

run_in_executor предназначен для использования в asyncio, поэтому его возвращаемое значение - asyncio Future, которым манипулирует поток, выполняющий цикл событий asyncio. Так как ваш код вращается в while циклы, не ожидая ничего, не давая цикл событий возможность работать на всех, эффективно блокирует цикл событий. Будущее остается "ожидающим", потому что обратный вызов, который должен был бы обновиться, должен вызываться циклом событий, который в настоящее время не работает - он просто находится в очереди.

Замена time.sleep(0.1) на await asyncio.sleep(0.1), вероятно, решит проблему. Но тогда вам не нужно while петля на всех; Поскольку будущее асинхронно ожидается, вы можете await его напрямую:

await future
# Do some stuff, with the future done.

await приостанавливает текущую сопрограмму до тех пор, пока не завершится будущее, предоставляя другим задачам шанс на выполнение в это время Возвращает значение будущего или распространяет исключение.

Альтернативой является вовсе не использовать asyncio, а напрямую использовать concurrent.futures. Таким образом вы получите ожидаемую семантику потоков (истинное "фоновое" выполнение) и Future которая работает соответственно.

# keep this in a global variable, so that the same executor
# is reused for multiple calls
executor = concurrent.futures.ThreadPoolExecutor()

# later, submit foo to be executed in the background
future = executor.submit(foo)

Это будущее concurrent.futures которое поддерживает ожидание такого результата:

result = future.result()

Кроме того, с таким будущим ваш исходный код будет работать без изменений.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню