comme je l’ai dit c’est pas tant pour des performance, mais c’est surtout l’attente, pour être tranquille,
On est d’accord là-dessus.
je dois être a 30 threads (ça permet a l’OS de gérer ses affaire a coté) et ça prends 15 min mais demain je vais avoir beaucoup de thread a faire tourner (pas forcement en même temps
Je t’avoue que j’ai du mal à voir. Tu pourrais me montrer le code (même épuré) que tu as pour le moment ?
Tu as combien d’URL à aller chercher ? Si tu en as énormément, même avec un pool d’exécution de 30 threads parallèles, ça va prendre du temps, en effet.
a la rigueur l’async pourrait être une solution, je remplace la partie "synchrone" par un verrou gevent ? je connais pas
Effectivement, l’asynchrone permet de résoudre ce genre de problème, surtout quand on s’attend à faire beaucoup d’appels externes avec une latence élevée comme en l’occurrence. Le Python asynchrone (Python 3.5+) peut le faire nativement, ou via une lib externe (gevent).
Le modèle de pool est implémentable avec gevent. Voici ce qu’en dit leurs docs :
A pool is a structure designed for handling dynamic numbers of greenlets which need to be concurrency-limited. This is often desirable in cases where one wants to do many network or IO bound tasks in parallel.
Je pense qu’on est dans le thème
Avec le Python asynchrone, avec asyncio.gather
on ferait ça :
async def get(url):
data = await http_get(url)
do_something(data)
async def process_all_urls():
await asyncio.gather(
get(url_1),
get(url_2),
...
get(url_n)
)
Si tu as vraiment beaucoup d’URL, le modèle de pool reste quand même plus intéressant, je pense. En effet, « lancer » des milliers de coroutines asynchrones dans un gather
n’est peut-être pas une bonne idée (je n’en ai jamais fait l’expérience réelle, c’est une supposition).
Avec les pool, tu peux garder cette idée de faire paquet par paquet, et ainsi mieux gérer les ressources pour éviter de les saturer. Si tu utilises asyncio.gather
, tu peux gérer ça à la main avec un boucle :
async def process_all_urls(all_urls):
for chunk in chunked(all_urls):
await asyncio.gather(*[get(u) for u in chunk])
Dans mon exemple, on admet que chunked
serait une fonction qui retourne une liste séparée tous les n éléments (par exemple 100, comme ici) :
all_urls = [u1, u2, u3, u4, ..., u1000]
chunked(all_urls) = [
[u1, ..., u100],
[u101, ..., u200],
...,
[u901, ..., u1000],
]
Et ainsi on traiterait les appels 100 par 100 sur un total du 1000, par exemple.