@Maxwell012

Как вызвать асинхронную функцию с синхронной функции?

Я первый раз столкнулся с созданием потоков в пайтон. Я пишу бота для тг с помощью библиотеки aiogram, внутри одной асинхронной функции я создаю новый поток что бы выполнить сторонний код и не заставлять основной поток ожидать. Так выходит что после выполнения стороннего кода мне надо отправить сообщение юзеру в тг, а отправка сообщение подразумевает под собой вызов асинхронной функции, то есть мне нужно теперь с нового синхронного потока вызвать асинхронный. Я достаточного много читал на форумах различных ответов, но я так и не пришел к решению.
Вот мой код:
async def load_link(message : types.Message, state: FSMContext):
    await state.finish()

    thr = threading.Thread(target=run_test, args=(data, message), name='thr-1').start()


def run_test(data, message):
    main.main(data[0]['surname'], data[0]['email'], data[0]['time'], message.text)
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(result(message))  
    loop.close()


async def result(message):
    document = types.InputFile('Result.jpg')
    await bot.send_document(chat_id=message.from_user.id, document=document, caption='Тест пройден ', reply_markup=kb_start_client)
    os.remove('Result.jpg')


Данный код возвращает ошибку: RuntimeError: Timeout context manager should be used inside a task
У меня вопросы такого характера (прошу извиниться за возможную тупость но я новичок в этом):
1) Можно ли как то реализовать то что я написал выше или как можно это упростить и сделать более рациональным?
2) Если я вызываю асинхронную функцию с синхронной я правильно понимаю что мне не надо ставить Lock или Rlock, потому что асинхронная функция и так будет выполняться одним потоком или это не так?
3) И возможно ли вообще создавать новые потоки асинхронными (я нигде не видел что бы так можно было делать, но это бы упростило мою задачу)
4) Как лучше именовать новые потоки которые будут создаваться в процессе?
  • Вопрос задан
  • 2570 просмотров
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Если твой вызов асинхронной функции сводится к реакции на завершение выполнения синхронного кода, то используй run_in_executor(), он позволяет выносить код хоть в процесс, хоть в поток (смотря какой executor используешь), и оборачивает его в обычную асинхронную корутину, которую можно await-ить. Для простых случаев есть asyncio.to_thread(), но тут вопрос в природе выполняемого кода. CPU-bound код на питоне будет занимать GIL, тормозя остальные потоки (эта общая болезнь для потоков в питоне).

А вообще есть асинхронные версии селениума, типа aioselenium или selenium-async. Можешь попробовать одну из них.

Если же тебе нужно вызвать асинхронную корутину именно посреди синхронного кода в другом потоке, то run_coroutine_threadsafe() может помочь. Пример из доков на питон.
async def coro_func():
     return await asyncio.sleep(1, 42)

# Later in another OS thread:

future = asyncio.run_coroutine_threadsafe(coro_func(), loop)
# Wait for the result:
result = future.result()
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы