Как правильно использовать функцию asyncio run_coroutine_threadsafe? - PullRequest
2 голосов
/ 07 февраля 2020

Я пытаюсь понять модуль asyncio и провожу около часа с функцией run_coroutine_threadsafe, я даже пришел к рабочему примеру, он работает как положено, но работает с несколькими ограничениями.

Прежде всего, я не понять, как правильно вызывать asyncio l oop в главном (любом другом) потоке, в примере, который я называю с помощью run_until_complete, и дать ему сопрограмму, чтобы он занимался чем-то, пока другой поток не даст ему сопрограмму. Какие у меня есть другие варианты?

В каких ситуациях мне приходится смешивать асинхронный и многопоточный режим (в Python) в реальной жизни? Поскольку, насколько я понимаю, asyncio должен выполнять многопоточность в Python (из-за GIL, а не операций ввода-вывода), если я ошибаюсь, не сердитесь и делитесь вашими предложениями.

Python версия 3.7 / 3.8

import asyncio
import threading
import time


async def coro_func():
    return await asyncio.sleep(3, 42)


def another_thread(_loop):
    coro = coro_func()  # is local thread coroutine which we would like to run in another thread

    # _loop is a loop which was created in another thread

    future = asyncio.run_coroutine_threadsafe(coro, _loop)
    print(f"{threading.current_thread().name}: {future.result()}")
    time.sleep(15)
    print(f"{threading.current_thread().name} is Finished")


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    main_th_cor = asyncio.sleep(10)
    # main_th_cor  is used to make loop busy with something until another_thread will not send coroutine to it
    print("START MAIN")
    x = threading.Thread(target=another_thread, args=(loop, ), name="Some_Thread")
    x.start()
    time.sleep(1)
    loop.run_until_complete(main_th_cor)
    print("FINISH MAIN")

1 Ответ

4 голосов
/ 07 февраля 2020

Прежде всего, я не понимаю, как правильно вызвать asyncio l oop в основном (любом другом) потоке, в примере я вызываю его с помощью run_until_complete и даю сопрограмму, чтобы он был занят с чем-то, пока другой поток не даст ему сопрограмму. Какие еще есть варианты?

Это хороший вариант использования для loop.run_forever(). L oop будет работать и обслуживать сопрограммы, которые вы отправляете, используя run_coroutine_threadsafe. (Вы даже можете передавать такие сопрограммы из нескольких потоков параллельно; вам никогда не нужно создавать более одного события l oop.)

Вы можете остановить l oop из другого потока, вызвав loop.call_soon_threadsafe(loop.stop).

В каких ситуациях мне приходится смешивать асинчо и потоки (в Python) в реальной жизни?

В идеале их не должно быть. Но в реальном мире они действительно возникают; например:

  • Когда вы вводите asyncio в существующую большую программу, которая использует потоки и блокирует вызовы и не может быть преобразована в asyncio одновременно. run_coroutine_threadsafe позволяет регулярному коду блокировки использовать asyncio.

  • Когда вы имеете дело со старыми API "asyn c", которые используют потоки под капотом и вызывают предоставленные пользователем API из других потоков. Есть много примеров, таких как Python собственные multiprocessing.

  • Когда вам нужно вызвать функции блокировки, которые не имеют асинхронного c эквивалента из асинхронного - например, CPU- связанные функции, устаревшие драйверы баз данных и тому подобное. Это не вариант использования для run_coroutine_threadsafe, здесь вы бы использовали run_in_executor, но это еще один пример смешивания потоков и асинхронности.

...