hu? avec asyncio, chaque activité s'exécute l'une après l'autre. On n'a pas de parallélisme mais la garantie que le bout de code qui s'exécute est le seul à changer l'état global du programme. Avec le threading, on ne sait pas quand l'activité sera interrompue, et on est obligé de verrouiller certaines partie de code pour s'assurer de la cohérence de l'état. Avec le multiprocessing, c'est encore autre chose: sauf à utiliser de la mémoire partagée sur des types simples, ce sont des messages qui passent entre les processus: on sait ce qui est partagé et ce qui ne l'est pas.
Le GIL n'apporte pas grand chose là dedans: il limite la capacité utilisable à un CPU.
On peut éventuellement utiliser des structures de bases comme les listes au lieu de Queue pour échanger des données entre les threads (certaines opérations étant sérialisées par le GIL, pas la peine d'ajouter des verrous par dessus)... mais comme on ne contrôle pas trop ce que fait et fera le GIL, c'est dangereux pour la maintenance du code.
Si on peut organiser le boulot en pipeline avec des unités workables qui passent d'une file d'attente à l'autre après certains traitements, il n'est pas utile d'avoir autant de threads que d'unités de traitement... et au lieu d'avoir une seule file d'attente et des découpages sur les IO (mais pas que) comme le propose asyncio, on aura plus de flexibilité.
La différence est que dans un cas, il faut se prendre le chou pour faire le design de la solution asynchrone alors qu'avec asyncio on aura un truc prêt à l'emploi... mais on se retrouve toujours avec des files d'attentes à vider.
note: quand les threads n'existaient pas, on faisait ce genre de design avec select comme cheville ouvrière pour ordonnancer les traitements (et beaucoup plus de design pour fabriquer des abstractions qui pouvaient s’accommoder du caractère assez primitif de select).
- W
Partager