Synchronisation de threads en python

Problème d'alternance

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Hello les gens,

Voilà pour un programme perso, j'ai un petit problème de synchronisation entre plusieurs threads :

J'utilise python pour réaliser mon programme.

Le problème est le suivant : j'ai 3 threads (2 threads + le programme principal) : deux threads "joueurs", et un thread "serveur"

J'aimerais via des sémaphores (des Locks en fait) faire une sorte d'alternance comme suit : les joueurs jouent (peut importe l'ordre), puis passent la main au serveur, qui traite les demandes des joueurs, puis rend la main aux joueurs, puis les joueurs jouent etc etc

Le problème semble simple et pourtant je n'arrive pas à l'implémenter :/

Normalement, il suffit de 3 locks : un pour le serveur, et un pour chaque joueur, lorsqu'un joueur veut jouer, il bloque son lock et celui du serveur, et libère quand il a fini, et lorsque le serveur process, il lock tous les joueurs et son propre mutex, et libre le tout quand tout est fini ?

merci d'avance pour votre aide, j'ai essayé plusieurs configurations sans succès :(

+0 -0

Je plussoie mon VDD, pas de threads si ça n'apporte rien, le prix en terme de complexité de programmation est trop élevé pour qu'on les utilise à tord et à travers (surtout que les mutex c'est hyper lent).

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+0 -0

Edit : précisions : ce qui suit concerne seulement l'utilisation des threads ou subprocess pour effectuer des calculs massifs en parallèle. Les traitements effectuant des entrées/sorties (transactions client/serveur, interface homme/machine etc) ne sont pas très impactés par le GIL.

Je plussois également. Voici une comparaison chiffrée sur un problème voisin : un module main qui gère deux calculs assez lourds exécutables en parallèle. L'idée avec les threads était de paralléliser les calculs (sur un PC quadri-core sous windows 7). Résultats :

  • la version sérialisée (les calculs sont éxécutés successivement) sert de référence sur un cas un peu lourd en traitements (283 secondes d'exec)

  • version avec threads : temps d'éxécution multiplié par plus de 20 !

  • version avec processus : temps d'exécution multiplié par 3 …

En fait, les interpréteurs python sont basés sur un principe dit Global Interpreter Lock (GIL) qui bloque tout calcul parallèle : les threads sont exécutés séquentiellement avec un gros overhead dù à la gestion des mécanismes des threads. (voir stackoverflow sur ce sujet). Avec les processus, c'est mieux car chaque process s'exécute dans une tâche séparée de l'OS (windows pour moi) mais lance un interpréteur python dans chaque tâche d'où une grosse pénalité au démarrage. De plus sous win, il faut mettre le programme en priorité haute pour que l'OS réserve un core à chaque process ! Et pour couronner le tout, en version de base sérialisée, l'OS - ou les couches basses - optimisent plutôt bien l'utilisation des cores en exécutant des parties de code en parallèle !

Ma conclusion sur cette expérience : en python, fuir les threads, et si on veut vraiment parallèliser, utiliser les processus mais sans garantie de meilleures performances.

Édité par rozo

+0 -0
Staff

Ma conclusion sur cette expérience : en python, fuir les threads, et si on veut vraiment parallèliser, utiliser les processus mais sans garantie de meilleures performances.

Je ne suis pas tout à fait d'accord sur cette conclusion. Il faut juste procéder au cas par cas, selon la nature du programme, comme pour toute optimisation.

Les threads restent extrêmement utiles en Python pour implémenter des chaines de producteurs/consommateurs indépendants et modulaires, d'autant plus si la chaîne de traitement varie d'une input à l'autre.

En termes de perfs, ils sont la solution la plus efficace pour parallèliser des taches denses en IO.

Les subprocesses, quant à eux, sont incapables de se passer efficacement des données entre eux, et c'est ce qui va le plus souvent les ralentir.

I was a llama before it was cool

+0 -0
Staff

Les threads, même avec le GIL, restent aussi très pratique dans des domaines comme les GUI où il est ainsi facile de séparer la vue du reste et ainsi ne pas bloquer l'UI pendant une opération longue.

Dans tous les cas, pour le reste, les subprocesses sont très pratique pour paralléliser des taches "gros grains". Typiquement un map-reduce sur des taches longues se fait facilement.

Enfin la meilleur façon de gagner du temps de traitement est, en général, d'être humble et de se dire que ce problème est peut être déjà en partie implémenté quelque part. Par exemple sur du calcul numérique utiliser intelligemment des libs comme numpy fait gagner en général beaucoup plus de temps de traitement que de tenter une parallélisation d'un code.

BTW, vis a vis du PO, le problème n'est même pas là. Son soucis semble être un problème conceptuel et c’était le sens de ma première remarque : pourquoi utiliser des threads alors qu'il veut un fonctionnement séquentiel ? A part complexifier inutilement l'application, il n'y a pas grand intérêt. Les threads ne sont justement pas fait pour ça et ça explique pourquoi il galère à les faire tourner séquentiellement.

Édité par Kje

+0 -0

@nohar et @Kje : je suis d'accord avec vous, les threads et subprocess peuvent avoir leur utilité.

Ma réaction est un peu brutale et ne concerne que mon cas (optimisation du temps d'exécution par des calculs parallèles). Ce qui m'a assez agacé, c'est qu'il n'est pas fait mention clairement du GIL dans la doc standard (chap 17) et que ce n'est qu'après avoir programmé assez laborieusement la version avec threads que je me suis aperçu du pb qui est un peu mis sous le tapis.

Ceci étant, je comprends très bien que les développeurs des interpréteurs Python qui ont déjà à se colleter avec la récursion, les closures et autres joyeusetés fort compliquées préférent éviter les gros pbs posés par les traitements multi-tâches se partageant des ressources, et ça me paraît plus sérieux de faire le choix 'on ne fait pas de parallélisme' plutôt que de bricoler des choses approximatives.

Les subprocesses, quant à eux, sont incapables de se passer efficacement des données entre eux, et c'est ce qui va le plus souvent les ralentir.

Effectivement, les échanges par les pipes dans les subprocess n'ont pas l'air très rapides.

+0 -0

Ma conclusion sur cette expérience : en python, fuir les threads, et si on veut vraiment parallèliser, utiliser les processus mais sans garantie de meilleures performances.

rozo

Mais quand même, les devs de Eve Online utilisent du (Stackless) Python pour une bonne partie du code serveur pour utiliser les threads comme des bourrins, et visiblement ça marche plutôt bien.

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte