Преобразовать функцию потока в asyncio

#python-3.x #multithreading #python-asyncio

#python-3.x #многопоточность #python-asyncio

Вопрос:

Мне нужно получить некоторые цены для внешнего API, одну за другой для дюжины объектов, и это займет 2-3 секунды для каждого запроса, так что это может стать довольно долгим.

Я (вроде) знал, как сделать многопоточность на python, я реализовал это, и это работает нормально, и это довольно быстро.

Затем я недавно обнаружил asyncio, и, похоже, это может быть полезно в моей ситуации вместо открытия нескольких потоков. Итак, я попытался «преобразовать» свой многопоточный код в код, использующий asyncio, как вы можете видеть ниже, прочитав несколько примеров. Но когда testOne не работает и ошибка Task exception was never retrieved . Я почистил код для лучшего понимания (дайте мне знать, если вам понадобится дополнительная информация).

 from threading import Thread
import asyncio


### ASYNC MULTI THREAD ####
def prixMulti(client, symbol, prix):                                                                           
    prix[symbol] = # API price request using client

def testMulti(client, sql):
    prix = {}
    objects = # Database request using sql
    listeThread = []
    for object in objects:
        listeThread.append(Thread(target=prixMulti, args=(client, object['name'], prix)))
    for t in listeThread:
        t.start()    
    for t in listeThread:
        t.join()
    print(prix)

#### ASYNC ONE THREAD ####
async def prixOne(client, symbol):
    return #same API price request using client

async def prixOneWait(client, symbol, prix):
    prix[symbol] = await prixOne(client, symbol)

def testOne(client, sql):
    prix = {}
    objects = # Database request using sql
    tasks = []
    loop = asyncio.get_event_loop()
    for object in objects: 
        tasks.append(loop.create_task(prixOneWait(client, prix, object['nom'] )))
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    print(prix)

# Some code to initialise client and sql 

testMulti(client, sql)
testOne(client, sql)
  

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

1. «тот же запрос цены API» в prixOne звучит не совсем правильно. Вы должны использовать асинхронный API и await его результат. В частности, вы не должны вызывать блокирующие функции из async def , и вы почти наверняка захотите чего-то ожидать (или функцией с таким же успехом может быть sync).

2. Я не могу изменить API, поскольку он внешний, возможно ли сделать его асинхронным ?!

3. Вы не можете сделать API асинхронным как пользователь API; он должен быть разработан и реализован как асинхронный с нуля. Самое близкое, что вы можете сделать, это использовать loop.run_in_executor для передачи функции пулу потоков, но тогда вы снова используете потоки под капотом и не получаете преимуществ asyncio. Если у вас есть только API синхронизации, я бы порекомендовал concurrent.futures модуль, в котором есть пул потоков и приятные примитивы для работы с ним.