Как в питоне запустить параллельно функцию в 2020 году?

хотел бы знать как в 2020году правильно запускать задачу в python3+ параллельно?

Задача имеется ввиду функция, которая будет исполняться на отдельной нити параллельно(т.е. multiprocessing не подходит).

Честно говоря, в интернете кроме как вызвать Thread и запустить его демоном(так я и делаю сейчас), больше ничего толкового не находится. Но кажется мне, что все-таки так делать это слишком ретроградно. Нагуглил тут pykka, и создание актора примерно так:

class Adder(pykka.ThreadingActor):
    def add_one(self, i):
        print(f'{self} is increasing {i}')
        return i + 1

Но опять же не хватает квалификации понять: будет ли это выполняться параллельно? В Python я новичек совсем, помогите... Может это совсем невозможно т.к. GIL и т.д.

Можно свести задачу к следующему: в одном потоке делать кое-что, в другое потоке по таймеру, вызывать еще одну функцию - в ней посылать rpc-запросы:

ПОТОК 1: блокирующая функция

ПОТОК 2: по таймеру вызываем rpc_send()


Ответы (1 шт):

Автор решения: eri

call_later, call_at, call_soon и тд выполняется в основном потоке

import asyncio

loop = asyncio.get_event_loop()

def timer(loop):
    loop.call_later(1, timer, loop)
    print('тыц')

timer(loop) # первый тыц сразу

loop.run_forever()

loop.run_forever() - блокирует, так что остальной код надо писать также на тасках asyncio.

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

По условиям задачи есть блокирующая функция - лучше переделать её на неблокирующий вариант. Асинкио предоставляет возможности для любого IO сделать асинхронный код.

Если сделать это не удается, то блокирующий кусок выносится в тред

import asyncio

loop = asyncio.get_event_loop()

def blocking_code():
    time.sleep(1000)

def timer(loop):
    loop.call_later(1, timer, loop)
    print('тыц')

timer(loop) # первый тыц сразу
task = loop.create_task(loop.run_in_executor(
    None, blocking_code)) 

loop.run_forever()

При этом GIL ещё работает. Обычно это не проблема, но можно его обойти через процессы

pool = concurrent.futures.ProcessPoolExecutor()

... loop.run_in_executor(pool, blocking_code) ...

P.S. Никогда не выполняйте блокирующий код в основном потоке хотябы потому что "программа не отвечает, завершить процесс?"

→ Ссылка