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 :

[py 3.8] process start message bref ca marche jamais


Sujet :

Python

  1. #1
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut [py 3.8] process start message bref ca marche jamais
    Bonjour
    j'ai fait un exemple auto porteur
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
     
        def __init__(self):
            multiprocessing.Process.__init__(self)
            self._stopper = multiprocessing.Event()
     
        def stopit(self):
            self._stopper.set()
     
        def stopped(self):
            return self._stopper.is_set()
     
        def run(self):
            while True:
                if not self.stopped():
                    time.sleep(1)    
                else: return
     
    class Updater:
     
        def __init__(self):
            print("creation updater")
            self.__fluxs = []
     
        def __del__(self):
            print("destrution updater")
     
        def dispose(self):
            for f in self.__fluxs:
                f.stopit()
                f.join()
            self.__fluxs.clear()
     
        def __iadd__(self, flux):
            #flux.notifyFunc = self.changed
            self.__fluxs.append(flux)
            return self
     
        def startFluxs(self):
            for f in self.__fluxs:
                f.start()
     
        def changed(self):
             pass
     
    class Flux(SProcess):
     
        def __init__(self):
            SProcess.__init__(self)
            self.notifyFunc = None
            print("creation flux")
     
        def __del__(self):
            print("destrution flux")
     
        @property
        def notifyFunc(self): return self._notifyFunc
     
        @notifyFunc.setter
        def notifyFunc(self, value): self._notifyFunc = value
     
     
    def simmulation():
        print("creation")
        u = Updater()
        f1 = Flux()
        f2 = Flux()
        u += f1
        u += f2
        print("start all flux")
        u.startFluxs()
        print("pause")
        time.sleep(10)
        print("dispose updater")
        u.dispose()
     
    if __name__ == "__main__":   
        print("-DEBUT---------------------------------------------------")
        simmulation()
        print("-END-----------------------------------------------------")
    j'ai j'ai donc deux soucis :

    Premier
    si je decommente la ligne 38 (flux.notifyFunc = self.changed)
    j'arrive plus a démarrer (cannot pickle 'weakref' object)

    Le second
    que je regarde la destruction
    ben ca détruit deux fois les class flux comme si le passage avait ete dupliqué
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    -DEBUT---------------------------------------------------
    creation
    creation updater
    creation flux
    creation flux
    start all flux
    pause
    dispose updater
    destrution flux
    destrution flux
    destrution updater
    destrution flux
    destrution flux
    -END-----------------------------------------------------
    y'a quoi dans mon (magnifique) code qui coince ?
    si vous avez une idée

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par ikeas Voir le message
    j'ai fait un exemple auto porteur
    Hum... je ne pige absolument pas le but de ce truc. Peut-être parce qu'il y a trop de commentaires...

    Citation Envoyé par ikeas Voir le message
    Le second
    que je regarde la destruction
    ben ca détruit deux fois les class flux comme si le passage avait ete dupliqué
    alors déjà chez-moi (xubuntu64) le comportement n'est pas reproduit. Voici ce qui se passe à la fin...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    pause
    dispose updater
    destrution flux
    destrution flux
    destrution updater
    -END-----------------------------------------------------
    ... et donc pas de duplication comme mentionné.

    Après on peut partir sur l'hypothèse de père mort avant le fils ou autres mais ça reste difficile à investiguer...

    Citation Envoyé par ikeas Voir le message
    Premier
    si je decommente la ligne 38 (flux.notifyFunc = self.changed)
    j'arrive plus a démarrer (cannot pickle 'weakref' object)
    Déjà est-ce que tu veux appeler la méthode changed, ou simplement stocker sa référence? Parce que si tu veux l'appeler, il lui faut des parenthèses d'action => flux.notifyFunc = self.changed(). Et si tu veux juste stocker sa référence alors là encore en ce qui concerne le but de l'opération...
    Sinon j'ai mis le premier point après le second car chez-moi là encore, avec ou sans commentaire, avec ou sans parenthèses d'action, le code fonctionne sans erreur (et sans duplication de __del__)


    Sinon question Python pur, déjà on peut simplifier la méthode run()
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def run(self):
    	while not self.stopped(): time.sleep(1)
    Ensuite il ne faut pas oublier que la délégation s'applique à toutes les méthodes héritées, pas seulement le __init__(). Si comme je le pense la méthode (par exemple) run() est une surcharge de celle dont on hérite, il faut alors récupérer aussi l'originelle via super().run() quelque part.
    Donc la méthode s'écrirait ainsi:
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def run(self):
    	super().run()
    	while not self.stopped(): time.sleep(1)
    Et pareil pour toutes les autres méthodes éventuellement surchargées. La délégation ce n'est pas une option. Sinon on ne surcharge pas une méthode.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    je vais t'expliquer le principe

    j'ai un updater qui factorise les changement liés a plusieurs flux
    donc il control les flux
    le += de celui met en place pour le flux sa fonction de notification le changed

    en gros quand un flux pourrait avoir un changement il ferrait self.notifyFunc() qui appellerait le changed puisqu'on a flux.notifyFunc = self.changed

    sauf que évidement on a l'erreur de weakref

    NOTA : j'ai modifié le code pour n'essayer qu'avec un seul flux et la ca marche comme si le partage de self.changed entre les deux process posait problème

    pour le second problème je suis sur Windows avec python 3.8

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    je vais te refaire une maquette
    sur le principe suivant

    l'updateur est le maitre des flux
    chaque flux connait son updateur dans le membre parent
    et dans sa methode running (surcharge de celle de Sprocess)
    il appel le parent.changed

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    import time
    import weakref
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
     
        def __init__(self):
            multiprocessing.Process.__init__(self)
            self._stopper = multiprocessing.Event()
     
        def stopit(self):
            self._stopper.set()
     
        def stopped(self):
            return self._stopper.is_set()
     
        def run(self):
            while True:
                if not self.stopped():
                    time.sleep(1) 
                    self.running()
                else: return
     
        def running(self): pass
     
    class Updater:
     
        def __init__(self):
            print("creation updater")
            self.__fluxs = []
     
        def __del__(self):
            print("destrution updater")
     
        def dispose(self):
            for f in self.__fluxs:
                f.stopit()
                f.join()
            self.__fluxs.clear()
     
        def __iadd__(self, flux):
            print(f"Ajoute flux {hash(flux)}")        
            self.__fluxs.append(flux)
            flux.parent = self
            return self
     
        def startFluxs(self):
            for f in self.__fluxs:
                print(f"start flux {hash(f)}")            
                f.start()
     
        def changed(self):
            print("CHANGED")
            pass
     
    class Flux(SProcess):
     
        def __init__(self):
            SProcess.__init__(self)
            self.parent = None
            print(f"creation flux {hash(self)}") 
     
        def __del__(self):
            print(f"destrution flux {hash(self)}") 
     
        @property
        def parent(self): return self._parent
     
        @parent.setter
        def parent(self, value): 
            print("update notify func")
            self._parent = value
     
        def running(self):
            print(f"Set {hash(self)}")
            if not self.parent is None:
                self.parent.changed()
            else: 
                print("dropped")
     
     
    def simmulation():
        print("creation")
        u = Updater()
        f1 = Flux()
        f2 = Flux()
        u += f1
        u += f2
        print("start all flux")
        u.startFluxs()
        print("pause")
        time.sleep(5)
        print("dispose updater")
        u.dispose()
     
    if __name__ == "__main__":   
        print("-DEBUT---------------------------------------------------")
        simmulation()
        print("-END-----------------------------------------------------")

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ikeas Voir le message
    je vais t'expliquer le principe

    j'ai un updater qui factorise les changement liés a plusieurs flux
    donc il control les flux
    le += de celui met en place pour le flux sa fonction de notification le changed

    en gros quand un flux pourrait avoir un changement il ferrait self.notifyFunc() qui appellerait le changed puisqu'on a flux.notifyFunc = self.changed

    sauf que évidement on a l'erreur de weakref
    Ok. Je vois un peu mieux. Mais qqchose me chagrine inconsciemment sans que j'arrive à mettre le doigt dessus.
    En fait l'addition reçoit un flux en paramètre, modifie ce flux pour y stocker le changed d'updater, puis ajoute ce flux dans son tableau interne. Quelque part je sens que c'est là un souci. Parce que que se passe-t-il si le updater reçoit un flux déjà terminé? C'est là que le weakref doit arriver.
    De plus si j'ajoute plusieurs fois le même flux à l'updater, celui-ci stockera le même flux autant de fois que je l'ajoute. Je sens qu'il y a là un autre souci qui peut interférer. Il me semble que l'addition n fois ne devrait pas ajouter n fois le même flux. Donc ton updater doit connaitre les flux gérés pour checker les ajouts multiples.
    Essaye de remplacer self.__fluxs = [] par self.__fluxs = set() et écrire ton __add__ ainsi
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    def __add__(self, flux):
    	if flux not in self.__fluxs:
    		flux.notifyFunc = self.changed
    		self.__fluxs.add(flux)
    	# if
    	return self
    # __add__()
    Il faudrait qu'il puisse aussi détecter un flux déjà terminé mais je ne vois pas comment faire...

    Sinon tu remarqueras que j'ai remplacé __iadd__ par __add__. En effet, le __add__ prend en charge et l'addition et l'addition implicite, tandis que le __iadd__ ne prend en charge que l'addition implicite. Ainsi avec __add__ tu peux écrire au choix u+=f1 ou u=u+f1 ce qui correspond aux spécifications de l'opérateur "+" devant accepter les deux écritures.
    Bref le __add__ est obligatoire si tu veux gérer l'addition et on ne met de __iadd__ que si son comportement doit différer du __add__.
    Ensuite pour conserver la commutativité de l'addition, tu es obligé de définir un __radd__ qui te permet d'écrire u = f1+u. Il n'a pas besoin de tout reprendre puisqu'il peut parfaitement utiliser le __add__ déjà écrit comme def __radd__(self, flux): return self+flux mais il doit exister.
    Cela peut aider.

    Citation Envoyé par ikeas Voir le message
    et dans sa methode running (surcharge de celle de Sprocess)
    Si surcharge alors délégation obligatoire
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def running(self):
    	super().running()
    	pass		# Si tu veux montrer que la fonction n'est pas terminée...
    # running()

    Citation Envoyé par ikeas Voir le message
    chaque flux connait son updateur dans le membre parent
    Si l'updater est assuré d'exister à la création du flux, tu pourrais le lui passer au __init__ ce qui supprimerait ces __add__ qui me semblent inutiles (une addition qui ne peut se faire qu'une fois...). Ainsi le flux connaitrait le updater et toutes ses méthodes (y compris la méthode changed).
    Exemple plus explicite
    Code python : 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
     
    	def __init__(self):
    		super().__init__()
    		self._stopper = multiprocessing.Event()
     
    	def stopit(self):
    		self._stopper.set()
     
    	def stopped(self):
    		return self._stopper.is_set()
     
    	def run(self):
    		super().run()
    		while not self.stopped(): time.sleep(1)	
     
    class Updater:
    	fluxs=property(lambda self: list(self.__fluxs))
     
    	def __init__(self):
    		print("creation updater", self)
    		self.__fluxs = set()
     
    	def __del__(self):
    		print("destrution updater", self)
     
    	def add(self, f): self.__fluxs.add(f)
    	def remove(self, f): self.__fluxs.discard(f)
     
    	def dispose(self):
    		for f in self.__fluxs:
    			f.stopit()
    			f.join()
    		self.__fluxs.clear()
     
    	def startFluxs(self):
    		for f in self.__fluxs: f.start()
     
    	def changed(self):
    		 pass
     
    class Flux(SProcess):
     
    	def __init__(self, updater):
    		super().__init__()
    		self.__updater=updater
    		self.__updater.add(self)
    		print("creation flux", self)
     
    	def __del__(self):
    		self.__updater.remove(self)
    		print("destrution flux", self)
     
            @property
    	def notifyFunc(self): return self._notifyFunc
     
            @notifyFunc.setter
    	def notifyFunc(self, value): self._notifyFunc = value
     
     
    def simmulation():
    	print("creation")
    	u = Updater()
    	f1 = Flux(u)
    	f2 = Flux(u)
    	print("start all flux")
    	u.startFluxs()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	u.dispose()
     
    if __name__ == "__main__":   
    	print("-DEBUT---------------------------------------------------")
    	simmulation()
    	print("-END-----------------------------------------------------")
    Chez-moi ça fonctionne...

    Citation Envoyé par ikeas Voir le message
    pour le second probleme je suis sur windows avec python 3.8
    Hum... du multiprocess sous Windows...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    en fait les flux sont pas démarrés déjà quand je fais les start
    je crois que le problème viens du partage de référence sur le le parent
    puisque chaque flux connait le même parent (un seul updateur)
    et que manifestement le partage de ressource sur du multiprocessing en python c'est particulier

    je sais pas si avec mon code initial avec le parent ca marche chez toi

    j'ai repris ton code j'ai le meme soucis avec le weakref
    tu es sous 3.8 ?

  7. #7
    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,

    Citation Envoyé par ikeas Voir le message
    pour le second problème je suis sur Windows avec python 3.8
    Par défaut sur Linux, la création des autres processus se fait via fork alors qu'elle se fait par copie sous Windows. Qui dit copie, dit sérialisation d'objets via pickle (à renseigner si ça ne marche pas tout seul).

    Pour le reste, soit vous écrit code comme on le sent et on peut constater que multiprocessing imposera ses règles derrière lesquelles on devra courir, soit on découvre les règles imposées par multiprocessing et on en tient compte pour écrire un code qui fera avec.

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

  8. #8
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    et il n'y a pas de solutions simple pour partager des objets d'un processus a l'autre ?
    j'ai reussi a faire un truc avec les manager
    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
    BaseManager.register('ParentClass', Updater)
    	manager = BaseManager()
    	manager.start()
    	print("creation")	
    	u = manager.ParentClass()
    	f1 = Flux(u)
    	f2 = Flux(u)
    	print("start all flux")
    	f1.start()
    	f2.start()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	f1.dispose()
    	f2.dispose()
    mais stocker les flux dans l'updateur j'ai une erreur
    Pickling an AuthenticationString object is disallowed for security reasons
    quand je veux le rajouter au tableau

  9. #9
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ikeas Voir le message
    en fait les flux sont pas démarrés déjà quand je fais les start
    Ca doit pouvoir se gérer avec is_alive() ou autre méthode de process...

    Citation Envoyé par ikeas Voir le message
    je crois que le problème viens du partage de référence sur le le parent
    puisque chaque flux connait le même parent (un seul updateur)
    Ca ne devrait pas poser de souci si, comme je le pense, l'updateur joue le rôle d'une espèce de tour de contrôle. On crée l'updateur puis on y associe les flux à leur création.

    Citation Envoyé par ikeas Voir le message
    et que manifestement le partage de ressource sur du multiprocessing en python c'est particulier
    Pas en python, c'est sous Windows que c'est particulier. Python ne fait que s'appuyer sur son OS.

    Citation Envoyé par ikeas Voir le message
    je sais pas si avec mon code initial avec le parent ca marche chez toi
    Sisi, le résultat que j'ai montré dans mon premier post c'était celui de ton code qui fonctione nickel sous Linux. Ensuite j'ai commencé à le charcuter.

    Citation Envoyé par ikeas Voir le message
    j'ai repris ton code j'ai le meme soucis avec le weakref
    tu es sous 3.8 ?
    3.10.
    Mais je viens d'installer python 3.8 sur une VM Windows, et avec ton premier code, ou avec mon dernier, même problème.
    Le souci vient de start() du flux lancé par le updater(). Il semble qu'en multiprocess Windows, le flux récupéré par le updater soit mal récupéré (il est récupéré en weakref=référence faible)
    Si je rajoute une surcharge de start ainsi...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def start(self):
    	print("starting...", self)
    	#super().start()
    ... tout marche nickel. Mais si je décommente le super().start() alors souci analogue au tien. Mais encore une fois seulement avec zindow. Lui en matière de multiprocess il se traine un sacré passif qui n'est pas prêt de s'arranger...

    J'ai tenté de remplacer def add(self, f): self.__fluxs.add(f) par def add(self, f): self.__fluxs.add(copy.deepcopy(f)) (j'avais eu un souci une fois avec un connecteur MySQL que j'avais résolu via le module copy) mais ici que dalle.

    Je suis en train d'examiner l'objet Process voir si on peut récupérer son coeur d'une façon ou d'une autre. Je vais essayer aussi avec pickle...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  10. #10
    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 ikeas Voir le message
    et il n'y a pas de solutions simple pour partager des objets d'un processus a l'autre ?
    Utiliser des threads à la place de process est une solution "simple" pour partager des objets.
    Avec des processus séparés, on peut avoir de la mémoire commune (mais il y a du boulot pour en faire des objets).

    Et quitte à me répéter, pour autant que la solution au problème que vous cherchez à résoudre passe par du multiprocessing, il va falloir apprendre à faire avec...

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

  11. #11
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    voila le code que j'ai pour window
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
     
    	def __init__(self):
    		super().__init__()
    		self._stopper = multiprocessing.Event()
     
    	def stopit(self):
    		self._stopper.set()
     
    	def stopped(self):
    		return self._stopper.is_set()
     
    	def start(self):
    		super().start()
     
    	def run(self):
    		super().run()
    		while not self.stopped(): time.sleep(1)	
     
    class Updater:
    	fluxs=property(lambda self: list(self.__fluxs))
     
    	def __init__(self):
    		print("creation updater", self)
    		self.__fluxs = set()
     
    	def __del__(self):
    		print("destrution updater", self)
     
    	def add(self, f): self.__fluxs.add(f)
    	def remove(self, f): self.__fluxs.discard(f)
     
    	def dispose(self):
    		for f in self.__fluxs:
    			f.stopit()
    			f.join()
    		self.__fluxs.clear()
     
    	def startFluxs(self):
    		for f in self.__fluxs: f.start()
     
    	def changed(self):
    		 pass
     
    class Flux(SProcess):
     
    	def __init__(self, updater):
    		super().__init__()
    		self.__updater=updater
    		self.__updater.add(self)
    		print("creation flux", self)
     
    	def __del__(self):
    		self.__updater.remove(self)
    		print("destrution flux", self)
     
            @property
    	def notifyFunc(self): return self._notifyFunc
     
            @notifyFunc.setter
    	def notifyFunc(self, value): self._notifyFunc = value
     
     
    def simmulation():
    	print("creation")
    	u = Updater()
    	f1 = Flux(u)
    	f2 = Flux(u)
    	print("start all flux")
    	u.startFluxs()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	u.dispose()
     
    if __name__ == "__main__":   
    	print("-DEBUT---------------------------------------------------")
    	simmulation()
    	print("-END-----------------------------------------------------")
    en fait même avec le super().start() ca fonctionne pas non plus (enfin chez moi)

    le problème c'est que je développe sur Windows dans un premier temps (pour des raisons pratiques) pour cibler sur linux dans un second temps

    en fait ce que je voulais faire était simple au départ
    - chaque flux disposait d'un compteur interne count
    - pour simplifier on imagine que dans sa boucle il incrémentait aléatoirement ce compteur en ajoutant +1 +2 +3 (par exemple pour tester)
    - il notifiait qu'il avait changé a l'updater par le biais de la fonction changed
    - l'updater demandait alors a tous les flux leurs valeurs de count et faisait une totalisation

    je voulais faire une jolie structure mais manifestement c'est pas simple

  12. #12
    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 ikeas Voir le message
    je voulais faire une jolie structure mais manifestement c'est pas simple
    Utilisez des threads, ce sera plus simple.
    Après si vous pensez que ça marchera mieux parce que vous avez essayé de faire joli... c'est votre droit mais vous perdez votre temps.

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

  13. #13
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ikeas Voir le message
    en fait même avec le super().start() ca fonctionne pas non plus (enfin chez moi)
    Normal, c'est le fait que le updater() ne possède pas le vrai flux qui fait que le flux ne peut pas démarrer.

    le problème c'est que je développe sur Windows dans un premier temps (pour des raisons pratiques) pour cibler sur linux dans un second temps
    Ben... Avec VirtualBox tu peux émuler les machnes de ton choix.

    je voulais faire une jolie structure mais manifestement c'est pas simple
    J'ai réussi. Je suis parti du fait que puisque updater n'arrivait pas à récupérer le flux créé à l'extérieur, alors il devait créer les flux en interne

    Code python : 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #!/usr/bin/env python3
    # coding: utf-8
     
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
    	def __init__(self):
    		super().__init__()
    		self._stopper = multiprocessing.Event()
     
    	def stopit(self):
    		self._stopper.set()
     
    	def stopped(self):
    		return self._stopper.is_set()
     
    	def run(self):
    		super().run()
    		while not self.stopped(): time.sleep(1)	
     
    class Updater:
    	def __init__(self):
    		print("creation updater", self)
    		self.__fluxs = set()
     
    	def __del__(self):
    		print("destrution updater", self)
     
    	def flux(self): self.__fluxs.add(Flux(self))
     
    	def dispose(self):
    		for f in self.__fluxs:
    			f.stopit()
    			f.join()
    		self.__fluxs.clear()
     
    	def startFluxs(self):
    		for f in self.__fluxs: f.start()
     
    	def changed(self):
    		 pass
     
    class Flux(SProcess):
    	def __init__(self, updater):
    		super().__init__()
    		print("creation flux", self)
    		self.__updater=updater
     
    	def __del__(self):
    		print("destrution flux", self)
     
     
    def simulation():
    	print("creation")
    	u = Updater()
    	u.flux()
    	u.flux()
    	print("start all flux")
    	u.startFluxs()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	u.dispose()
     
    if __name__ == "__main__":   
    	print("-DEBUT---------------------------------------------------")
    	simulation()
    	print("-END-----------------------------------------------------")

    C'est moins souple (obligé de lui demander de créer chaque flux sans pouvoir le créer nous-même) mais là au-moins l'updater peut contrôler les flux.

    Après je suis d'accord avec wiztricks. Puisque zindow ne sait pas gérer le multiprocess, autant par par les thread. Au-moins ce sera mieux géré aussi bien par zin que par Linux.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  14. #14
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    avec ton dernier code j'ai le même probleme sur Windows avec
    cannot pickle 'weakref' object
    au niveau du démarrage des flux

    je vais essayer de trouver une autre solution

  15. #15
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ikeas Voir le message
    avec ton dernier code j'ai le même probleme sur Windows avec
    cannot pickle 'weakref' object
    au niveau du démarrage des flux
    T'as raison. Je viens de refaire un test et pareil, mon dernier code plante sous Windows.
    Ce qui s'est passé: j'ai changé un peu le code que j'ai testé en pensant que c'était sans conséquence et ça ne l'est pas (désolé)
    Voici le code qui fonctionne
    Code python : 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #!/usr/bin/env python3
    # coding: utf-8
     
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
     	def __init__(self):
    		super().__init__()
    		self._stopper = multiprocessing.Event()
     
    	def stopit(self):
    		self._stopper.set()
     
    	def stopped(self):
    		return self._stopper.is_set()
     
    	def run(self):
    		super().run()
    		while not self.stopped(): time.sleep(1)	
     
    class Updater:
    	fluxs=property(lambda self: list(self.__fluxs))
     
    	def __init__(self):
    		print("creation updater", self)
    		self.__fluxs = set()
     
    	def __del__(self):
    		print("destrution updater", self)
     
    	def flux(self): self.__fluxs.add(Flux())
     
    	def dispose(self):
    		for f in self.__fluxs:
    			f.stopit()
    			f.join()
    		self.__fluxs.clear()
     
    	def startFluxs(self):
    		for f in self.__fluxs: f.start()
     
    	def changed(self):
    		 pass
     
    class Flux(SProcess):
    	def __init__(self):
    		super().__init__()
    		print("creation flux", self)
     
    	def __del__(self):
    		print("destrution flux", self)
     
    def simmulation():
    	print("creation")
    	u = Updater()
    	u.flux()
    	u.flux()
    	print("start all flux")
    	u.startFluxs()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	u.dispose()
     
    if __name__ == "__main__":   
    	print("-DEBUT---------------------------------------------------")
    	simmulation()
    	print("-END-----------------------------------------------------")

    Ensuite, j'ai abandonné les tests windows et suis repassé sous Linux (plus pratique pour moi) et me rappelant que le flux devait connaitre l'updater, je l'ai rajouté dans son objet et modifié la méthode "flux()" de l'updater pour qu'elle passe son self au flux créé
    Code python : 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #!/usr/bin/env python3
    # coding: utf-8
     
    import time
    import multiprocessing
     
    class SProcess(multiprocessing.Process): 
    	def __init__(self):
    		super().__init__()
    		self._stopper = multiprocessing.Event()
     
    	def stopit(self):
    		self._stopper.set()
     
    	def stopped(self):
    		return self._stopper.is_set()
     
    	def run(self):
    		super().run()
    		while not self.stopped(): time.sleep(1)	
     
    class Updater:
    	def __init__(self):
    		print("creation updater", self)
    		self.__fluxs = set()
     
    	def __del__(self):
    		print("destrution updater", self)
     
    	def flux(self): self.__fluxs.add(Flux(self))
     
    	def dispose(self):
    		for f in self.__fluxs:
    			f.stopit()
    			f.join()
    		self.__fluxs.clear()
     
    	def startFluxs(self):
    		for f in self.__fluxs: f.start()
     
    	def changed(self):
    		 pass
     
    class Flux(SProcess):
    	def __init__(self, updater):
    		super().__init__()
    		print("creation flux", self)
    		self.__updater=updater
     
    	def __del__(self):
    		print("destrution flux", self)
     
    def simulation():
    	print("creation")
    	u = Updater()
    	u.flux()
    	u.flux()
    	print("start all flux")
    	u.startFluxs()
    	print("pause")
    	time.sleep(5)
    	print("dispose updater")
    	u.dispose()
     
    if __name__ == "__main__":   
    	print("-DEBUT---------------------------------------------------")
    	simulation()
    	print("-END-----------------------------------------------------")

    Mais (erreur) je n'ai pas retesté ensuite. Et aujourd'hui que je teste sous Windows ça plante pareil.
    Le souci ne vient donc pas de l'updater qui n'arrive pas à récupérer le flux créé, mais du flux qui n'arrive pas à récupérer l'objet updater. Et là je vois pas comment régler le souci.

    Citation Envoyé par ikeas Voir le message
    je vais essayer de trouver une autre solution
    Oui, les thread dont parlait wiztricks, c'est un autre axe de recherche qui serait intéressant d'explorer.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  16. #16
    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 Sve@r Voir le message
    Ensuite, j'ai abandonné les tests windows et suis repassé sous Linux (plus pratique pour moi)
    En changeant la méthode de démarrage des process à "spawn" (sous Linux) on devrait constater le même soucis que sous Windows.

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

  17. #17
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    je pense que je vais abandonner la notification directe par méthode des flux vers l'updater
    pour passer sur une solution ipc standard (faut que je me replonge dans les ipc)
    l'idée serait que les flux quand ils changent leurs valeurs "count" envoient une notification a l'updater qui se trouve dans le main process
    les messages (interruption) ca me semble pas top type sigint etc
    si vous avez une idée simple

  18. #18
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ikeas Voir le message
    pour passer sur une solution ipc standard (faut que je me replonge dans les ipc)
    L'outil n'a pas le même but.
    La communication interprocess (pipes en général) a pour but de faire communiquer des process existants. Les ipc (Internal Process Communication) ont pour but de faire communiquer un process existant avec un process n'existant pas encore ou n'existant plus.
    Le process ayant une info à transmettre la dépose dans l'ipc de son choix (file de messages, mémoire partagée) qui se comporte comme une espèce de coffre fort. Ensuite il peut disparaitre. Puis plus tard, le processus devant récupérer l'info va la récupérer dans l'ipc.
    Mais (à moins d'un quiproquo), les ipc auxquelles je pense (msq, shm, sem) ne sont disponibles que sous Linux. Et dans ce cas je ne vois pas pourquoi tu ne restes pas directement sous Linux

    Citation Envoyé par ikeas Voir le message
    l'idée serait que les flux quand ils changent leurs valeurs "count" envoient une notification a l'updater qui se trouve dans le main process
    si vous avez une idée simple
    fork + pipe?

    Voici un petit tp de communication socket entre deux programmes (tiré d'un de mes cours en C et recodé en Python)
    Le serveur
    Code python : 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #!/usr/bin/env python3
    # coding: utf-8
     
    import socket
    import sys
    import os
     
    class cSocket:
    	def __init__(self, sockfile):
    		try: os.remove(sockfile)
    		except FileNotFoundError: pass
    		self.__connect=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    		self.__connect.bind(sockfile)
    		self.__dialog=None
    		print("socket {} prête à écouter sur le fichier {}".format(self.__connect, sockfile))
    	# __init__()
     
    	# Ecoute
    	def listen(self):
    		self.__connect.listen(1)
    		while True:
    			self.__dialog=self.__connect.accept()[0]
    			pid=os.fork()
    			if pid == 0:
    				self.__client()
    				exit(0)
    			# if
    		# while
    	# listen()
     
    	# Gestion du client connecté
    	def __client(self):
    		print("client pid={} - Entrée émise".format(os.getpid()))
    		data=None
    		while data != "EOT":
    			data=self.__dialog.recv(1024).decode("utf-8")
    			print(
    				"client pid={}: Message: [{}]{}".format(
    					os.getpid(),
    					data,
    					" => Arrêt du client" if data == "EOT" else "",
    				)
    			)
    		# while
    		print("client pid={} - Entrée raccrochée".format(os.getpid()))
    	# __client()
    # class cSocket
     
    if __name__ == "__main__":
    	cSocket(sys.argv[1]).listen()

    Le client
    Code python : 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
    #!/usr/bin/env python3
    # coding: utf-8
     
    import socket
    import sys
     
    class cSocket:
    	def __init__(self, sockfile):
    		self.__dialog=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    		self.__dialog.connect(sockfile)
    		print("socket {} connectée sur le fichier {}".format(self.__dialog, sockfile))
    	# __init__()
     
    	def dialogue(self):
    		while True:
    			msg=input("Tapez votre message (EOT pour quitter): ")
    			self.__dialog.send(msg.encode("utf-8"))
    			if msg == "EOT":
    				print("Client teminé")
    				break
    			# if
    		# while
    	# dialogue()
    # class cSocket
     
    if __name__ == "__main__":
    	cSocket(sys.argv[1]).dialogue()

    Principe: on lance le serveur en lui donnant en paramètre le fichier socket (ex ./serveur.py /tmp/xxx) puis dans une autre fenêtre on lance le client en lui donnant le même fichier.
    Puis dans le client on tape ce qu'on veut et tout ce qu'on tape est affiché par le serveur. Ensuite quand on veut arrêter le client on tape EOT.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  19. #19
    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 ikeas Voir le message
    si vous avez une idée simple
    Répéter étant la base de la pédagogie, il faut comprendre comment fonctionne multiprocessing (ou n'importe quelle bibliothèque) pour savoir comment reformuler son besoin avec ce qu'elle propose.
    Vous faites "à l'envers": partir d'une idée et l'espoir que ce sera simple à réaliser (alors que vous n'avez aucune idée de ce qui est simple/compliqué)...

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

  20. #20
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    oui je vais utiliser les sockets ca me parait plus simple pour faire une notification primaire

Discussions similaires

  1. [VB.NET 1.1] Ping silencieux avec Process.Start()
    Par toniolol dans le forum Windows Forms
    Réponses: 6
    Dernier message: 30/09/2008, 13h35
  2. Réponses: 3
    Dernier message: 27/09/2007, 13h55
  3. Process.Start génère une erreur
    Par jerome.fortias dans le forum C#
    Réponses: 6
    Dernier message: 17/09/2007, 15h31
  4. Problème de Process.Start depuis une page ASPX
    Par LudVichzme dans le forum ASP.NET
    Réponses: 3
    Dernier message: 30/04/2007, 20h05
  5. [JSP][Process] Récupérer message
    Par hedgehog dans le forum Servlets/JSP
    Réponses: 2
    Dernier message: 20/07/2005, 13h33

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