IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Threads & Lock


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Inscrit en
    Décembre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 42
    Par défaut Threads & Lock
    Voici un petit exemple tout simple pour mettre en oeuvre les threads.
    Mais je ne comprends pas pourquoi le résultat dans la console est aussi bordélique.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    from threading import Thread
    from threading import Lock
    import sys
     
     
    G = 0
     
    class MyPrinter (Thread):
     
       def __init__ (self,txt, nbr):
          Thread.__init__(self)
          self.txt = txt
          self.nbr = nbr
     
       def run (self):
          global G
          for i in range(self.nbr):
            lock = Lock()
            lock.acquire(0)
            try:
                G = G+1
                print "%s_%i" % (self.txt, G)
                sys.stdout.flush()
            finally:
                lock.release()
     
     
     
     
    ThreadA = MyPrinter ("AAA",10)
    ThreadA.start()
     
    ThreadB = MyPrinter ("BBB",10)
    ThreadB.start()
     
    print "\nTheEnd\n"
     
    ThreadA.join()
    ThreadB.join()
     
    print "\nTheRealEnd\n"
    Le flush était une tentative désespérée pour caler proprement les retours à la ligne.
    Quelqu'un a-t-il une solution ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    AAA_1
    AAA_2BBB_3
    TheEnd
     
     
     
    BBB_4AAA_5
     
    AAA_6
    AAA_7BBB_8
     
    AAA_9
    AAA_10BBB_11
     
    AAA_12BBB_13
     
    BBB_14
     AAA_15
     BBB_16
     AAA_17
    BBB_18
    BBB_19
    BBB_20
     
    TheRealEnd

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Un lock sert à sérialiser l'accès à une ressource partagée.
    Dans votre cas, les ressources partagés par les threads sont G et sys.stdout.
    Il faut donc définir un lock qui sera acquis 'avant de'... et non un lock à chaque itération. Voir code ci dessous.
    - W

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    from threading import Thread
    from threading import Lock
    import sys
     
    lock_stdout = Lock()
     
    G = 0
     
    class MyPrinter (Thread):
     
       def __init__ (self,txt, nbr):
          Thread.__init__(self)
          self.txt = txt
          self.nbr = nbr
     
       def run (self):
          global G
          for i in range(self.nbr):
              with lock_stdout:
                G = G+1            # also serialized under same lock
                print "%s_%i" % (self.txt, G)
     
    ThreadA = MyPrinter ("AAA",10)
    ThreadA.start()
     
    ThreadB = MyPrinter ("BBB",10)
    ThreadB.start()
     
    print "\nTheEnd\n"
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre averti
    Homme Profil pro
    Inscrit en
    Décembre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 42
    Par défaut
    Merci beaucoup.
    Voici exactement ce que je voulais faire. Un exemple simple pour la prise en main des threads.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    from threading import Thread
    from threading import Lock
     
    lock_stdout = Lock()
     
     
    class MyPrinter (Thread):
     
       def __init__ (self,txt, nbr):
          Thread.__init__(self)
          self.txt = txt
          self.nbr = nbr
     
       def run (self):
          for i in range(self.nbr):
              with lock_stdout:
                print self.txt
     
    ThreadA = MyPrinter ("AAA",10)
    ThreadA.start()
     
    ThreadB = MyPrinter ("BBB",10)
    ThreadB.start()
     
    ThreadA.join()
    ThreadB.join()
    Résultat dans la console :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    Appuyez sur une touche pour continuer...
    Remarquons tout de même que sans les join(), Python crache une petit retour à la ligne à la fin de la thread principale :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    AAA
     
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    AAA
    BBB
    Appuyez sur une touche pour continuer...

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,
    J'ai lu votre question sur .join et ligne blanche mais je n'arrive pas à reproduire chez moi.
    Puisque vous débutez avec les threads, voici une autre façon d'écrire le même code.
    Note: votre code est très bien, je vous propose un autre "style" pour faire "pareil". L'idée est d'éviter lorsque ce n'est pas nécessaire de tout empiler dans une même classe.
    - W

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from threading import Thread
    from threading import Lock
    import time
     
    def myprint (m):
        if not hasattr(myprint, 'lock'):
            setattr(myprint, 'lock', Lock())
        with myprint.lock:
            print (m)
     
     
    def counter(text, count):
        for i in range(count):
            myprint('%s-%d' % (text, i))
            time.sleep(0.0) ## force resched
     
    if __name__ == '__main__':
        ta = Thread(target=counter, args=('aaa', 10))
        ta.start()
        tb = Thread(target=counter, args=('bbb', 10))
        tb.start()
     
        ta.join()
        tb.join()
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    En supprimant les '.join()', je n'ai pas le retour de ligne supplémentaire après le 1er 'AAA' sur PyScripteur, mais je l'ai en console.

    Par contre, si je regroupe les 2 '.start()', je ne l'ai plus:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    ...
    ThreadA = MyPrinter ("AAA",10)
    ThreadB = MyPrinter ("BBB",10)
     
    ThreadA.start()
    ThreadB.start()
    ...
    Mais je ne sais pas pourquoi. Problème de timing?

    Tyrtamos

  6. #6
    Membre averti
    Homme Profil pro
    Inscrit en
    Décembre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 42
    Par défaut
    Concernant le retour à la ligne.
    C'est une petite curiosité sans grande importance, mais c'est amusant et j'ai quand même regardé.

    Oui, effectivement, c'est dans la console que je l'ai constaté.

    Eh non! Quand je regroupe les .start() j'ai toujours ce retour à la ligne.
    Bizarre...

    Maintenant, je vous demande de considérer avec attention le programme suivant nommé return.py

    Lorsque je l'exécute, dans la console, j'obtiens ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    D:\CODE\IM\Python>return.py
     
    D:\CODE\IM\Python>
    Je crois que Python crache tout simplement un retour à la ligne quand il finit l'exécution d'un programme.

    Et si vous regarder le premier code source de cette discution, je crois que ce n'est pas sans rapport avec les print "\nTheEnd\n" et print "\nTheRealEnd\n"...
    D'où l'idée des .join() pour lui dire d'attendre que l'on ait fini le travail avant de cracher !

    Enfin, je je sais toujours pas comment vous avez résolu ce triste problème en regroupant les .start()

  7. #7
    Membre averti
    Homme Profil pro
    Inscrit en
    Décembre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 42
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    , voici une autre façon d'écrire le même code.
    Note: votre code est très bien, je vous propose un autre "style" pour faire "pareil". L'idée est d'éviter lorsque ce n'est pas nécessaire de tout empiler dans une même classe.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from threading import Thread
    from threading import Lock
    import time
     
    def myprint (m):
        if not hasattr(myprint, 'lock'):
            setattr(myprint, 'lock', Lock())
        with myprint.lock:
            print (m)
     
     
    def counter(text, count):
        for i in range(count):
            myprint('%s-%d' % (text, i))
            time.sleep(0.0) ## force resched
     
    if __name__ == '__main__':
        ta = Thread(target=counter, args=('aaa', 10))
        ta.start()
        tb = Thread(target=counter, args=('bbb', 10))
        tb.start()
     
        ta.join()
        tb.join()
    Si je comprends bien, le time.sleep(0.0) donne plus de chances au thread de passer la main ? C'est à dire que dans le cas où l'on aurait un ensemble de threads concurrentes plus complexes, cela aurait pour effet d'améliorer la fluidité de l'ensemble ?


    Pour ce qui est de cet autre style de programmation, je vous le dis franchement, je tombe des nues.
    Sincèrement, c'est bien d'échanger des points de vues dans les forums, c'est fait pour ça, il n'y a pas de problème. Mais je trouve ce style de programmation complètement abscons. Et je lutte très souvent contre !

    D'abord, j'écris une classe pour un petit problème de test parce que je sais qu'en situation réelle, pour un plus gros programme, j'utiliserai des classes.
    Alors autant voir tout de suite les choses comme ça.

    Ensuite, vous vous rendez compte que vous créez un attribut de fonction, ce qui est vraiment très spécifique à Python, et qui revient exactement au même que créer un attribut de classe.
    Pourquoi créer de la confusion quand les structures de classes sont devenues universelles et donc bien comprises pas tout le monde ?
    La syntaxe spécifique de Python, est dans ses grandes lignes d'une simplicité exemplaire. C'est une qualité qui justifie dans une large mesure le succès de Python.
    Mais d'un autre côté, la syntaxe de Python devient vite très compliqué dans son versant introspectif. (Tous les __kekchose__ ...)
    L'utilisation des choses bizarres de Python n'est recommandée que si elles permettent effectivement de faire des choses en plus.

    D'autre part, je ne vois pas l'intérêt de séparer myprint et counter.
    Il travaillent ensemble. Pour ma part, je les voit clairement comme faisant partie d'un même objet.

    Ensuite, je n'aime pas du tout la ligne ta = Thread(target=counter, args=('aaa', 10))
    Honnêtement on ne peut pas dire qu'avec son target et ses arguments passés en paramètres, ce soit plus intuitif ou plus joli que ThreadA = MyPrinter ("AAA",10)
    Non ? Vous ne trouvez pas ?

    Enfin, il n'y a vraiment qu'en Python qu'on trouve des trucs comme if __name__ == '__main__': pour indiquer le début d'un programme.

    Bon, j'aime bien Python, j'aime bien la programmation objets et les deux vont bien ensemble. Je ne vais quand même pas faire un plaidoyer de 10 pages pour la programmation objets.

    Ne prenez pas mal mon commentaire, mais franchement, les classes c'est génial. Je suis prêt à lire vos arguments si vous me répondez.

    Est-ce que la variable ta est plus claire que ThreadA ?

    Encore un point sur lequel je trouve la syntaxe de Python un peu compliquée, et ça concerne les classes cette fois.
    Est-ce que c'est vraiment sympa d'écrire les variables privées comme __MaVar=0, plutôt que MaVar=0 dans un bloc "private" qui n'existe pas ?

    Il faut que je m'arrête. J'arrête.

  8. #8
    Membre averti
    Homme Profil pro
    Inscrit en
    Décembre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 42
    Par défaut
    Ahrr..! Le retour à la ligne.

    C'est vrai que c'est ridicule de mettre des .join() à la fin du programme alors qu'il n'a plus rien à faire.

    Mais quand il sait qu'il n'a plus rien à faire, il crache. Je doute aussi que ce soit très "normal".

    Idée : Est-ce que ça ne serait pas plutôt la console qui crache quand elle reprend la main ?

    Edition :
    Je crois que lorsque vous dites que les threads ne sont pas "daemon", vous dites que ça ne peut pas être la console.

    Remarque :
    Le programme en C
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(void)
    {
       return 0;
    }
    fait le même retour à la ligne que le programme Python

  9. #9
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Citation Envoyé par FredOoo123 Voir le message
    Si je comprends bien, le time.sleep(0.0) donne plus de chances au thread de passer la main ? C'est à dire que dans le cas où l'on aurait un ensemble de threads concurrentes plus complexes, cela aurait pour effet d'améliorer la fluidité de l'ensemble ?
    Les threads de Python s'exécutent dans un même process à cause du GIL.
    Lorsqu'une thread "tourne" elle ne rendra la main qu'à la fin de son "slice" et dans ce genre de démo, vous risquez parfois de voir B s'exécuter avant A

    D'abord, j'écris une classe pour un petit problème de test parce que je sais qu'en situation réelle, pour un plus gros programme, j'utiliserai des classes.
    Alors autant voir tout de suite les choses comme ça.
    Dans le cas des threads vous avez des modèles de programmation qui n'ont rien à voir avec la POO. Celui utilisé dans votre cas est appelé "Workers". i.e. un paquet de fonctions/méthodes "parallélisables", auxquelles on va associer des ressources (les threads).
    Il n'y a pas de bonnes raisons pour que cette association soit 1:1.
    Ca dépendra des activités, de la capacité du CPU,... Paramètres difficiles à fixer à priori.

    Votre principe (partir sur des classes) est peut être acceptable pour des objets "passifs" mais peu réaliste lorsqu'on passe à des objets "actifs" - associé à des threads.

    Ensuite, vous vous rendez compte que vous créez un attribut de fonction, ce qui est vraiment très spécifique à Python, et qui revient exactement au même que créer un attribut de classe.
    En Python, les fonctions sont des "First Class Objects". Ce qui signifie qu'on peut leur associer un état comme aux modules/classes et autres instances. Faut-il se limiter aux classes conventionnelles parce que d'autres langages ne proposent pas ces constructions? Quel intérêt?
    Pourquoi créer de la confusion quand les structures de classes sont devenues universelles et donc bien comprises pas tout le monde ?
    Hum... Mes collègues US diraient: "if all you have is a hammer, everything looks like a nail"
    D'autre part, je ne vois pas l'intérêt de séparer myprint et counter.
    Il travaillent ensemble. Pour ma part, je les voit clairement comme faisant partie d'un même objet.
    Il y a pourtant une "console" sur laquelle on souhaite sérialiser les "print" et des "activités" x, y, z, ... qui utiliseront ou pas cette console.
    Comme nous voulons exécuter ces activités en parallèle, nous avons un troisième larron appelé threads qui entre en jeu...
    Il n'est pas "transparent" puisqu'il nous force à définir un verrou "global" associé à la "console".

    Les 3 entités "console", "activités" et "threads" sont distinctes.
    Il n'est pas interdit de les faire coexister dans une même classe... Après tout le programmeur fait ce qu'il veut...

    Mais va-t-on pouvoir hériter de la chose?
    Dans le cas particulier, la nécessité n'est pas évidente.
    Dans le cas général, l'héritage d'une telle chose est aussi délicat que celui d'un singleton.

    Vous me direz, oui mais on peut instancier...
    Certes! Mais votre premier "post" illustre les difficultés rencontrées à l'instanciation du mélange ces trois entités dans une même boite.

    POO et threads ne font pas bon si bon ménage, il faudra vous y résoudre.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Multi-thread] Comment lock l'acces a un containeur de la STL ?
    Par Izidor's dans le forum Threads & Processus
    Réponses: 5
    Dernier message: 14/10/2009, 12h09
  2. Thread et lock pour écrire dans un fichier?
    Par DarkHerumor dans le forum C#
    Réponses: 3
    Dernier message: 31/03/2009, 09h40
  3. Réponses: 5
    Dernier message: 14/08/2008, 11h25
  4. comment utiliser les lock dans les threads ?
    Par skad dans le forum Général Python
    Réponses: 2
    Dernier message: 15/07/2008, 14h28
  5. Thread et lock de tables
    Par babylone7 dans le forum Oracle
    Réponses: 6
    Dernier message: 27/06/2006, 17h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo