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

  1. #1
    Membre à l'essai
    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
    Points : 17
    Points
    17
    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 sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    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 à l'essai
    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
    Points : 17
    Points
    17
    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 sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    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 éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    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
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  6. #6
    Membre à l'essai
    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
    Points : 17
    Points
    17
    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
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Je crois que Python crache tout simplement un retour à la ligne quand il finit l'exécution d'un programme.
    J'ai testé sous OSX. Je suis incapable de reproduire la chose avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    osx:~ mps$ ./ex_foo10.py 
    osx: more ex_foo10.py
    # /usr/bin/env python
    Je n'ai pas pris le temps de chercher si le comportement de Python était définit dans ce cas, mais je doute que sortir une ligne vide soit "normal".
    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 !
    Dans le cas particulier ou le programme attend la terminaison des threads avant de sortir (i.e. pas pour faire autre chose) les .join ne servent à rien. Ca fait peut être plus propre, c'est tout.
    note: ce comportement peut être changé avec l'attribut daemon. Dans ce cas, par défaut, le programme sort sans attendre et le .join aide.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Effectivement, c'est en supprimant les join que la fin de ligne supplémentaire apparait de façon aléatoire.

    Pourtant, comme les threads ne sont pas "daemon", le programme principal est censé attendre pour se terminer que les threads le soient aussi: les join() ne devraient donc pas être nécessaires?

    Bizarre, bizarre...

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  9. #9
    Membre à l'essai
    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
    Points : 17
    Points
    17
    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.

  10. #10
    Membre à l'essai
    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
    Points : 17
    Points
    17
    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

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    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

  12. #12
    Membre à l'essai
    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
    Points : 17
    Points
    17
    Par défaut
    Pour ce qui est de l'écriture du code sous forme de classe, disons que c'est un goût personnel.

    J'ai aussi la conviction que des objets biens construits peuvent s'appliquer à touts les problèmes, même si c'est vrais, c'est un tout petit peu coûteux, not a hammer.

    Mais depuis le langage machine, en passant par l'assembleur, le Basic spaghetti, la programmation structurée, puis objet, jusqu'aux langages interprétés souvent peu typés, il y a un coût croissant accepté en contrepartie d'une simplification du code, de meilleurs méthodes de travaille des équipes séparées et de robustesse du code dès le moment de l'écriture.

    Sur ce dernier point, on m'a appris par exemple ceci :

    A.lock_acquire()
    ...
    A.lock_release()

    Est bien moins sûr que :

    A.lock() {
    ...
    }

    On m'a expliqué que "avant", on écrivait toujours selon la première méthode. Et lorsqu'il arrivait que l'on oublie le release (ça arrivait toujours, un jour ou l'autre) c'était un vrai casse-tête pour retrouver l'erreur. (erreur à l'exécution)
    Avec la seconde méthode on a une erreur de compilation et un numéro de ligne.

  13. #13
    Membre à l'essai
    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
    Points : 17
    Points
    17
    Par défaut
    Ceci dit, j'ai un autre problème de verrou :

    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
     
    import threading as MyChoice
    #import multiprocessing as MyChoice
     
    if   MyChoice.__name__=="threading"       : Parent = MyChoice.Thread
    elif MyChoice.__name__=="multiprocessing" : Parent = MyChoice.Process
     
     
    class CMyPrinter ( Parent ):
     
        lock_stdout = MyChoice.Lock()
     
        def __init__ ( self, txt, nbr ):
            super( CMyPrinter, self ).__init__()
            self.txt = txt
            self.nbr = nbr
     
        def run ( self ):
            for i in range( self.nbr ):
                with self.lock_stdout:
                    print( self.txt )
     
     
    if __name__ == '__main__':
     
        MyPrinter1 = CMyPrinter( "XXX", 10 )
        MyPrinter2 = CMyPrinter( "III", 10 )
     
        MyPrinter1.start()
        MyPrinter2.start()
     
        MyPrinter1.join()
        MyPrinter2.join()
     
        print( MyPrinter1.lock_stdout is MyPrinter2.lock_stdout )
    Tout va bien quand je choisi d'importer threading.

    Mais quand j'importe multiprocessing, l'affichage est bordélique, le verrou ne marche pas. (Il faut exécuter plusieurs fois, car parfois on a de la chance, on croit que ça marche)

    En dehors du problème du verrou, on ne m'en voudra pas d'avoir utilisé une grosse astuce avec mes if/elif juste après l'import.

    Je dirais : Attendu que la documentation de python déclare au chapitre 16.6.1.1 du fichier lié ici que "Process follows the API of threading.Thread" je me suis arrangé pour tester les deux API avec le même code et surtout un seul fichier.

    Autre remarque : La dernière ligne permet de vérifier que le verrou utilisé dans chaque instance de la classe est bien le même objet.

    Enfin, j'ai testé avec les versions 2.6.6, 2.7.1 et 3.2 de Python.
    Il n'y a pas de problème avec la 3.2.
    Mais pour des raisons indépendantes de ma volonté, je dois utiliser la 2.6.6.

    Quelqu'un a-t-il une explication ? une solution ?

    Message pour wiztricks:
    Le code suivant marche très bien... mais à la fin, mon projet doit vraiment être en objets.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from multiprocessing import Process, Lock
     
    def f ( l, txt, nbr ):
        for i in range(nbr):
            with l:
                print( "%s_%i" % (txt, i) )
     
    if __name__ == '__main__':
        lock = Lock()
        Process(target=f, args=( lock, "XXX", 10 )).start()
        Process(target=f, args=( lock, "III", 10 )).start()

  14. #14
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par FredOoo123 Voir le message
    On m'a expliqué que "avant", on écrivait toujours selon la première méthode. Et lorsqu'il arrivait que l'on oublie le release (ça arrivait toujours, un jour ou l'autre) c'était un vrai casse-tête pour retrouver l'erreur. (erreur à l'exécution)
    Avec la seconde méthode on a une erreur de compilation et un numéro de ligne.
    Le problème est que même sans oubliez le release, il n'est jamais certain que le release soit exécuté parce que le code entre les deux aura levé une exception et... l'état sera non prédictible/inconsistant.
    La seconde méthode "aligne" l'acquisition/release du verrou avec la gestion de la pile d'exécution: c'est ce qui permet de "garantir".

    Python utilisant une allocation mémoire basée sur un garbage collector rend impossible cette technique d'alignement sur la pile. Il faut utiliser un contrôle via try/catch qu'on peut encapsuler pour une mise en oeuvre avec with.

    Un des gros problèmes des informaticiens est que "le compliqué de la matière" nous pousse à nous réfugier derrière des recettes de cuisines en oubliant un peu le pourquoi, du comment,... le domaine d'application, les autres approches possibles. Si c'est un réflexe humain et tout à fait normal dans le contexte, s'y enfermer (quelque soit la bonne mauvaise raison de la chose) rend les progressions plus difficile.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  15. #15
    Membre à l'essai
    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
    Points : 17
    Points
    17
    Par défaut

  16. #16
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ajoute mon grain de sel.

    Sur un plan général, j'aime bien comprendre comment ça marche. Mais, à part ça, le moins qu'on puisse exiger, c'est que le seul respect du manuel garantisse le bon fonctionnement.

    Sur le plan du multiprocessing dont j'ai besoin, et sous Python 2.7, je viens de faire quelques essais et, nom d'un chien, il ne faut pas grand chose sous Windows pour que ça plante, même pour des programmes très simples. Et quand ça plante, même le gestionnaire des tâches ne répond plus... On doit redémarrer le pc "à la manivelle" (=retirer la prise ). Ça m'est arrivé trois fois hier, et ça ne me donne pas envie de continuer.

    Bref, la mise au point de programmes en multiprocessing est long et douloureux... J'espère que ça s'arrangera en Python 3.x.

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Sur un plan général, j'aime bien comprendre comment ça marche. Mais, à part ça, le moins qu'on puisse exiger, c'est que le seul respect du manuel garantisse le bon fonctionnement.
    Vous pouvez "espérer", "exiger", dans le contexte, ne me semble pas "raisonnable" sauf être déçu et passer beaucoup de temps à pester contre l'injustice du monde et des choses.

    Bref, la mise au point de programmes en multiprocessing est long et douloureux... J'espère que ça s'arrangera en Python 3.x
    La programmation distribuée ou répartie est un sujet très difficile. Ce n'est pas une question de langage de programmation mais liée à l'opportunité d'avoir pu construire (ou non) de bon repères à partir de vos expériences.
    Il faut du temps, des opportunités, et une ouverture d'esprit pour d'intégrer des paradigmes et leur contexte.
    L'important ici n'est pas le "comment" mais le "pourquoi" du comment.

    Message pour wiztricks:
    Le code suivant marche très bien... mais à la fin, mon projet doit vraiment être en objets.
    Hey... En python tout est déjà objet.
    Vous persistez à emballez ensemble des entités qui cohabitent mal dans une boîte appelée "class"... et dire voilà ce que doivent être les "objets".
    Vanité quand tu nous tiens

    Mon code marche sans soucis.
    Ce n'est pas sans raison que je vous l'ai proposé...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  18. #18
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Mon code marche sans soucis.
    Ce n'est pas sans raison que je vous l'ai proposé...
    Mais "fonctionner mieux" ne signifie pas qu'il soit "correct" dans un contexte "multiprocessing" ou nous avons plusieurs interpréteurs (et autant de factories de classes et de fonctions sans la protection des modules "singletons").

    Reprenons les entités 'console', 'activité', 'thread'.

    Le soucis, dans le contexte, est de s'assurer que le verrou associé à la 'console' est effectivement 'global'.

    Et pour qu'il soit global ou que tout le monde utilise le même, il faut l'initialiser une seule fois et le poser dans les différentes factories avant que 'multiprocessing' ne clone les différents modules.

    Dans les programming guidelines, c'est écrit ainsi:

    Explicitly pass resources to child processes

    On Unix a child process can make use of a shared resource created in a parent process using a global resource. However, it is better to pass the object as an argument to the constructor for the child process.

    Apart from making the code (potentially) compatible with Windows this also ensures that as long as the child process is still alive the object will not be garbage collected in the parent process. This might be important if some resource is freed when the object is garbage collected in the parent process.
    Mais:
    Global variables

    Bear in mind that if code run in a child process tries to access a global variable, then the value it sees (if any) may not be the same as the value in the parent process at the time that Process.start() was called.

    However, global variables which are just module level constants cause no problems.
    Tout ce charabia pour dire que sous UNIX l'espace des différents process et copy-on write alors que sous Window il est "différent". Faire une API homogène dans les deux environnements passe évidement par des "programming guidelines" mais çà reste du charabia...

    Mais dans notre cas, "on sait" que çà se passe côté "console" que je veux initialiser une seule fois et passer aux différente activités....

    Ce qui donne par exemple:
    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
    ## from threading import Thread
    ## from threading import Lock
    from multiprocessing import Process as Thread
    from multiprocessing import Lock
    import time
     
    print 'run globals'
     
    class MyPrint:
        def __init__(self):
            self._lock = Lock()
        def __call__(self, m):
            with self._lock:
                print (m)
     
     
    def counter(text, count, myprint):
        for i in range(count):
            myprint('%s-%d' % (text, i))
            time.sleep(0.0) ## force resched
     
    if __name__ == '__main__':
     
        myprint = MyPrint()  # la console...
     
        ta = Thread(target=counter, args=('aaa', 10, myprint))
        ta.start()
        tb = Thread(target=counter, args=('bbb', 10, myprint))
        tb.start()
     
        ta.join()
        tb.join()
    Une fois comprises les dépendances entre les 3 entités, on peut s'amuser à faire une seule classe pour cacher le tout... mais bof, un module est déjà "sort of class", l'utilisateur ne verra pas la différence.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  19. #19
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour wiztricks,

    Citation Envoyé par wiztricks Voir le message
    Vous pouvez "espérer", "exiger", dans le contexte, ne me semble pas "raisonnable" sauf être déçu et passer beaucoup de temps à pester contre l'injustice du monde et des choses.
    Je n'ai qu'un niveau d'exigence raisonnable: : on doit pouvoir faire du bon code avec le manuel et, bien sûr, des connaissances suffisantes en matière de conception et de développement.

    Des réflexions sur la philosophie des systèmes sont un plus, mais cela ne peut pas remplacer le manuel. Et si en plus les 2 se contredisent (ex: thread et class), on ne sait plus quoi faire. Dans ce cas, je n'écoute plus la philo et je retourne au manuel.

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Des réflexions sur la philosophie des systèmes sont un plus, mais cela ne peut pas remplacer le manuel. Et si en plus les 2 se contredisent (ex: thread et class), on ne sait plus quoi faire. Dans ce cas, je n'écoute plus la philo et je retourne au manuel.
    Les classes comme le manuel sont des trucs "statiques".
    C'est utile mais çà reste "descriptif".
    Les threads sont des objets dynamiques qui modifient la relation entre les "class" et l'espace "global" qui leur est associé.
    Dans le cas des threads, on peut encore s'en sortir.

    Avec les forks UNIX on a un avant/après le fork qui vont influer sur le contenu de l'espace global. Avec Windows, nous avons un autre schéma.
    Le problème est que ce n'est pas intuitif... Et que les "bugs" ne se révèlent que dans certaines conditions, "bien construire" suppose appréhender les points d'articulation entre les différents objets sous-jacents au code.
    Et comme ce sont des objets "système" et non Python... vous pouvez toujours lire la doc...
    - 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.
Page 1 sur 3 123 DernièreDernière

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