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 :

Crash aléatoire d'un thread


Sujet :

Python

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    243
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2008
    Messages : 243
    Points : 103
    Points
    103
    Par défaut Crash aléatoire d'un thread
    Bonjour,

    Depuis plusieurs jours, je suis confronté au crash aléatoire d'un thread dans une application qui comporte:
    1- une fenêtre de saisie et affichage
    2- un thread de calcul.

    Fort heureusement (ou malheureusement!) ces crashs se produisent très rarement; mais cela révèle l'existence d'un bug et j'aimerais bien corriger la situation.

    J'ai reproduit le problème sur un bout de code minimum.

    Le programme principal ouvre une fenêtre Tkinter dans laquelle il y a un curseur (Scale) et un label.

    Ce programme lance un thread de calcul qui tourne continuellement dans une boucle et qui:
    1- récupère la valeur affichée au curseur
    2- calcul une variable, dans le cas présent la variable rejoint progressivement la valeur affichée au curseur
    3- modifie la valeur de la variable dans le "main", ce qui provoque son affichage

    Voici le code du main:

    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
    # -*- coding: ISO8859-1 -*-
    #
    from Tkinter import *
    import threading
    from calcul import *
    import time
     
    from sys import modules
    progmain = modules['__main__']
     
    def afficher(varName, index, mode):  #  déclenchée lors d'un changement de my_var       
        label_my_var.configure ( text = 'ma variable : ' + '%6.1f' %my_var.get())     
     
    def set_var(my_var):  #  activée lors du mouvement du curseur
        my_cursor.event_generate('<Control-Z>')
     
    def stop_calcul():
        calcul_running.set(False)  # stop calcul when mainloop is terminated
        time.sleep (1.)             # attendre 1 seconde l'arrêt du calcul
        root.destroy()
     
    # main prog   ###################################
     
    root = Tk()
    # creation du curseur
    my_cursor = Scale(root, length=450, orient=HORIZONTAL, sliderlength =15,
            label ='ma consigne', from_=0, to=200, tickinterval =20,
            resolution =0.1,showvalue =1, command = set_var)
    my_cursor.pack()
    # creation de l'afficheur avec variable traçable
    label_my_var = Label (root, font=("Arial",14))
    label_my_var.pack()
    my_var = DoubleVar()  
    my_var.trace_variable("w", afficher)  # tout changement de my_var déclenche son affichage
     
    #  lancement du thread de calcul
    calcul_running=BooleanVar()  # le booléen permet d'arrêter le thread
    calcul_running.set(True)
    t1=threading.Thread(target=calcul)
    t1.start()
     
    #  execute mainloop
    root.protocol ("WM_DELETE_WINDOW",stop_calcul)
    root.mainloop()
    Et voici le code du thread de calcul:

    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
    # -*- coding: cp1252 -*-
     
    from math import *
    import time
     
    from sys import modules
    progmain = modules['__main__']
     
    def calcul():
     
    # initialisation
        step = 0.1  ;  temps = 0. ;   var = 0.
     
        while progmain.calcul_running.get()== True:
     
            progmain.my_var.set(var)    #  on renvoie var dans progmain.my_var, cette action déclenche
                                        #  l'affichage par changement de sa valeur
     
    # calcul d'une seconde fictive par pas de 0,1 seconde ################
     
            for i in range(0,10):       
                var0 = progmain.my_cursor.get() # récupérer la valeur du curseur toutes les 0.1 seconde
                temps = temps + step            #  calcul du temps fictif
                dvar = var0 - var               #  adjuster progressivement var à la valeur du curseur
                if (abs(dvar) <=  (5. * step) ) :
                    var = var0
                else :
                    var = var + 5.* step * dvar / abs (dvar)
                time.sleep(0.1)                 # les calculs sont remplacés par time.sleep
    Voici le genre de bug aléatoire qui est déclenché, notez qu'il est surprenant que la fenêtre soit intitulée "Visual C++"



    suivi du message suivant dans lequel la fenêtre est bien intitulée "pythonw.exe - erreur d'application"



    Il n'y a aucun message sur la fenêtre Python !!

    J'ajoute un point important :
    le crash se produit uniquement (quand il se produit!) lors d'un déplacement du curseur

    Merci d'avance à ceux qui me liront et surtout à ceux qui pourront me donner un coup de main pour résoudre ce problème.

    Bonne journée

  2. #2
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    bonjour,

    as tu essayé de le lancer via python.exe et non pythonw.exe pour voir si un autre message d'erreur avec traceback est affiché sur la console ?

    naïvement, je penserais que le problème viendrait de:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var0 = progmain.my_cursor.get()
    si jamais l'instruction intervient au moment même où tu fais bouger le curseur. ce qui expliquerait pourquoi le bug est rare et n'intervient que si tu déplace le curseur.

    du coup, ce serait un problème de synchronisation ?

    là je vois 2 idées: passer par une Queue.Queue pour synchroniser ou ajouter un try... except... autour de cette instruction afin d'attendre que le curseur ait arrêté de bouger pour continuer.

    mais compte tenu du message d'erreur que tu as, rien ne laisse présager que ce soit bien ce problème

    le message c++ runtime error n'est pas anormal dans le sens où Python est compilé avec Visual c++ sur windows et qu'il est installé avec quelques dll c++.

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    243
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2008
    Messages : 243
    Points : 103
    Points
    103
    Par défaut
    Bonjour Kango,

    Merci.

    Je soupçonnais aussi cette instruction depuis un certain temps, pour ne pas dire un temps certain

    Et tu as raison

    Je ne connais pas grand'chose à Python et je ne pensais pas que python.exe donne plus d'infos que pythonw.exe ...

    Donc voici le message de python.exe :
    on voit bien que le pb est sur cette ligne



    J'ai essayé try ... except ... et j'ai cru pendant 5 minutes que cela marchait mais non

    Maintenant, je suis nul alors j'ai fait ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    try : progmain.my_var.set(var)    
    except : pass
    Il faut peut-être détecter l'erreur ???

    Si tu as le temps, peux tu, stp, m'en dire un peu plus sur les Queue.Queue ?



    A+

  4. #4
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    ok, Queue.Queue est un peu plus lourd à mettre en place, et pas vraiment adapté à ton problème,

    je te propose plutôt un truc du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    while 1:
        try:
            var0 = progmain.my_cursor.get()
        except ValueError:
            time.sleep(0.01)
        else:
            break
    qui va rendre l'appel bloquant: c'est à dire qu'il va attendre que tu arrêtes de bouger pour récupérer la valeur.

    une autre approche consiste à conserver la valeur de var0 de l'itération précédente:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    oldvar0 = var0
    try:
        var0 = progmain.my_cursor.get()
    except ValueError:
        var0 = oldvar0

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    243
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2008
    Messages : 243
    Points : 103
    Points
    103
    Par défaut
    Merci Kango,

    ça a l'air de fonctionner

    J'apprécie ce forum, il y a vraiment des gens qui savent !

    Bonne journée

    PS : je ne mets "résolu" car je n'ai pas testé encore assez longtemps

  6. #6
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Intéressent...
    Cela montre t'il que dans la class variable tk la variable est vidée (none) pour prendre la nouvelle valeur ? Je n'ai rien vu de ce type dans Tkinter.py.
    Cela viens t'il de Tk ?

    Une idée ?

    @+
    Merci d'utiliser le forum pour les questions techniques.

  7. #7
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    je pense que cela vient en effet de la librairie sous-jacente en tcl/tk. en essayant de se mettre dans la logique du truc, je pense qu'elle considère qu'à partir du moment où le widget est activé par l'utilisateur, sa position est inconnue. ce qui n'est pas faux puisque l'utilisateur peut la mettre n'importe où

    sans trop m'avancer, je dirais que Tkinter n'est pas Thread-safe, c'est à dire qu'il n'est pas spécifiquement prévu qu'un thread essaie d'accéder à une information au moment même où elle se modifie. c'est pour cette raison que je pense que l'utilisation de Queue.Queue peut permettre de rendre Tkinter thread-safe.

    voici un article qui va dans ce sens d'ailleurs:

    http://effbot.org/zone/tkinter-threads.htm

    après, et c'est un avis très très perso, Tkinter c'est bien mais il est vraiment intéressant de regarder du côté d'autres librairies pour de l'interface graphique (pyqt, pygtk, wxpython).

  8. #8
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Citation Envoyé par kango Voir le message
    sans trop m'avancer, je dirais que Tkinter n'est pas Thread-safe
    Effectivement j'ai lu cela.

    Par contre il semble que l'utilisation de la class variable soit a proscrire dans le cadre de calculs en temps réel.

    @+
    Merci d'utiliser le forum pour les questions techniques.

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    243
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2008
    Messages : 243
    Points : 103
    Points
    103
    Par défaut
    Re,

    J'ai fait une petite modif pour voir en écrivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    oldvar0 = var0
    try:
         var0 = progmain.my_cursor.get() 
    except ValueError:
         print 'erreur'
         var0 = oldvar0
    Et il y a bien quelques messages 'erreur' qui s'impriment de temps en temps!!!
    Mais le programme ne plante plus

    Je ne comprends pas très bien pourquoi Tkinter vide la variable ..
    Ne serait il pas suffisant de garder la valeur et de la remplacer dès qu'une nouvelle valeur valide est présente, par exemple quand l'utilisateur relâche le bouton de la souris.
    De plus le widget Scale est défini avec une valeur mini et une valeur maxi, donc normalement, il ne doit pas y avoir de valeur inacceptable.

    J'ai regardé Queue.Queue ... c'est un peu dur pour mon niveau ...

    Quand au choix de Tkinter, il s'est fait sans grande évaluation préliminaire.

    Merci encore

    Bonne journée

  10. #10
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Points : 970
    Points
    970
    Par défaut
    oui Queue.Queue c'est un peu dur quand on débute même si en soit c'est assez simple. Il faut le voir comme un conteneur qui permet de "partager" des données dans plusieurs Thread différents.

    normal que le code t'affiche quelque fois 'erreur', c'est ce que l'on cherche à faire .

    quand il le fait, ça veut dire qu'il utilises toujours la consigne de l'itération précédente, je ne sais pas si c'est gênant pour toi

    quant au choix de Tkinter, il est tout à fait légitime lorsqu'on débute pour 2 raisons essentielles et liées:

    - c'est par défaut inclus dans Python
    - c'est presque toujours documenté dans les bouquins (papier ou électronique) que l'on utilise quand on débute.

    donc ce n'est pas du tout un reproche de ton choix.

    c'est un peu comme les gens qui utilises Windows parce c'est ce qu'il y a par défaut sur les ordinateurs et qui découvrent ensuite Linux. (oula c'est pas bien, je troll ).

  11. #11
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Puisque le get donne None pourquoi ne pas tester un wait-variable avant ?

    wait_variable(variable). Wait for the given Tkinter variable to change. This method enters a local event loop, so other parts of the application will still be responsive. The local event loop is terminated when the variable is updated (setting it to it's current value also counts).

    @+
    Merci d'utiliser le forum pour les questions techniques.

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    243
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2008
    Messages : 243
    Points : 103
    Points
    103
    Par défaut
    Citation Envoyé par kango Voir le message
    normal que le code t'affiche quelque fois 'erreur', c'est ce que l'on cherche à faire .

    quand il le fait, ça veut dire qu'il utilises toujours la consigne de l'itération précédente, je ne sais pas si c'est gênant pour toi
    Bonjour Kango,

    C'est bien pour cela que j'ai mis ce "print" ...
    Cela montre bien que quand le problème se produit, le try ... except permet de passer sans crash.

    Utiliser la valeur de l'itération précédente ne me gêne absolument pas ..

    C'est pourquoi j'ai choisi cette solution entre les deux que tu m'as indiquées.

    Merci encore

    Citation Envoyé par kango Voir le message
    quant au choix de Tkinter, il est tout à fait légitime lorsqu'on débute pour 2 raisons essentielles et liées:

    - c'est par défaut inclus dans Python
    - c'est presque toujours documenté dans les bouquins (papier ou électronique) que l'on utilise quand on débute.

    donc ce n'est pas du tout un reproche de ton choix.
    C'est tout à fait ça ... je suis fainéant et je ne suis pas allé chercher plus loin

    Citation Envoyé par kango Voir le message
    c'est un peu comme les gens qui utilises Windows parce c'est ce qu'il y a par défaut sur les ordinateurs et qui découvrent ensuite Linux.
    J'ai connu Unix dans les années 80/90 (Oh! là je suis vieux !) bien avant de connaitre Windows ....
    Et puis je me suis mis à Windows et trop fainéant pour revenir à Linux .. C'est toujours remis à la semaine prochaine

    Bonne journée
    A+

    PS: Pour PauseKawa ... utiliser la vieille valeur du curseur ne gêne absolument pour mon application ... le .get() s'exécute tous les 1/10 de seconde donc récupérer la commande de l'opérateur à +/- 0.1 seconde me va très bien.
    Mais je note l'idée du wait_variable ... qui sait ce que l'avenir me réserve?

  13. #13
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour jlg_47,

    Puisque c'est Tk (et son histoire de None) le souci pourquoi ne pas s'en passer ?
    Par contre set/get de la classe variable Tkinter ayant tout leurs intérêts voici une solution de rechange (pas terrible mais c'est l'idée).

    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
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    # class myvar for jlg_47
    # http://www.developpez.net/forums/d915461/autres-langages/python-zope/general-python/crash-aleatoire-thread/
    # Based on Tkinter variable class
    class myvar:
        _default = ""
        def __init__(self, master=None, value=None, name=None, trace=None, mode=None):
            if not master:
                master = self
            self._master = master
            if name:
                self._name = name
            else:
                self._name = None
            if value is not None:
                self._value=value
            else:
                self._value=self._default
            if trace:
                self._trace=trace
            else:
                self._trace=None
            if mode:
                self._mode=mode
            else:
                self._mode=None
            self._oldmode=None
            self._oldtrace=None
     
        def __str__(self):
            return str(self._name)
     
        def set(self, value):
            self._value=value
            if self._trace is not None and (self._mode == 'u' or self._mode == 'w'):
                self.savetrace()
                self._oldtrace()
            elif self._oldtrace is not None:
                self.restauretrace()
     
        def get(self):
            if self._trace is not None and (self._mode == 'u' or self._mode == 'r'):
                self.savetrace()
                self._oldtrace()
            elif self._oldtrace is not None:
                self.restauretrace()
            return self._value
     
        def trace_variable(self, mode, callback):
            self._mode=mode
            self._trace=callback
     
        trace = trace_variable
     
        def trace_vdelete(self):
            self._mode=None
            self._trace=None
     
        def trace_vinfo(self):
            return self._mode, self._trace
     
        def varinfo(self):
            return "Master", self._master, "Name", self._name, "Value", str(self._value), "Trace callback", self._trace, "Trace mode", self._mode
     
        # TODO For callback recursion
        def restauretrace(self):
            self._trace=self._oldtrace
            self._mode=self._oldmode
            self._oldtrace=None
            self._oldmode=None
     
        def savetrace(self):
            self._oldtrace=self._trace
            self._oldmode=self._mode
            self._trace=None
            self._mode=None
     
    if __name__ == "__main__":
        a=myvar()
     
        # Default value = ""
        print a.get()
        # Set a value
        a.set(10)
        # Get the value
        print a.get()
     
        # trace value
        def callback():
            # Please, don't use multiples get()/set() whith callback
            print 'a in callback', a.get()
            # a.set(30)
        a.trace('u', callback)
        a.set(20)
        print 'a after callback'
        print a.get()
     
        # Callback informations
        print 'Callback informations', a.trace_vinfo()
     
        # Name
        b=myvar(name='b')
        print 'b name', b
     
        # Variable informations
        print 'Variable informations'
        dir(b)
        print b.varinfo()
     
        # Delete callback
        a.trace_vdelete()
        print a.trace_vinfo()
    @+

    Edit pour

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        def __str__(self):
            return str(self._name)
    Et correction (presque...) de la récursion dans le callback

    @++
    Merci d'utiliser le forum pour les questions techniques.

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

Discussions similaires

  1. [JFileChooser] plantage aléatoire dans un Thread séparé
    Par RR instinct dans le forum Agents de placement/Fenêtres
    Réponses: 3
    Dernier message: 24/07/2009, 13h53
  2. dRedimensionne crash aléatoires
    Par xander412 dans le forum WebDev
    Réponses: 0
    Dernier message: 17/07/2009, 15h13
  3. [Ubuntu 8.10][Eclipse 3.2] Crash aléatoire très régulier
    Par ovh dans le forum Applications et environnements graphiques
    Réponses: 7
    Dernier message: 09/03/2009, 23h08
  4. Réponses: 10
    Dernier message: 11/01/2008, 11h10
  5. Crash aléatoires dans un programme quasi-totallement managé
    Par smyley dans le forum Général Dotnet
    Réponses: 1
    Dernier message: 09/11/2007, 02h22

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