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

Dotnet Discussion :

Présentation d'un tutoriel au sujet de l'utilisation du multithreading


Sujet :

Dotnet

  1. #1
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut Présentation d'un tutoriel au sujet de l'utilisation du multithreading
    Bonjour à tous,

    J'ai écrit un tutoriel à propos de l'utilisation du multithreading dans une application de traitement par lots (batch): http://immobilis.developpez.com/tuto...tielle-batchs/

    N'hésitez pas à me dire ce que vous en pensez. Si vous avez des questions n'hésitez pas à les poster ici, j'aurai plaisir à y répondre.

    Merci d'avance.

    Immo
    "Winter is coming" (ma nouvelle page d'accueil)

  2. #2
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Question bête mais pourquoi ne pas utiliser la TPL ?

  3. #3
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Salut
    Citation Envoyé par Nathanael Marchand Voir le message
    TPL ?
    La quoi?

    [EDIT]La Task Parallel Library? Ben je connaissais pas.

    A l'époque, j'ai trouvé une solution avec mes connaissances du moment et le Framework imposé (3.5). Bon, pour le tutoriel , j'ai utilisé le FW 4.5 et VS Ultimate. Je vais regarder ça.

    Merci
    "Winter is coming" (ma nouvelle page d'accueil)

  4. #4
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    pourquoi ne pas utiliser la TPL ?
    Effectivement, cette bibliothèque semble très pratique.

    Toutefois, je n'ai pas trouvé de méthode/implémentation pour reproduire le comportement "flux tendu". Il y a bien les "tâches de continuation" mais il faudrait gérer la possibilité de démarrer un nombre variable de tâches (ex: 10). Une fois finie, chaque tâche doit lancer la suivante jusqu'à ce que toute la pile ait été traitée. C'est faisable, mais du coup on ne profite pas forcement des capacités de la TPL.

    Sachant qu'on ne connait pas le nombre de tâches ni le nombre de flux simultanés, l'utilisation de "child task" ou "nested task" ne nécessiterait-il pas la création d'une fonction récursive? Il faut que je teste cela, mais il me semble que la tâche parent ne serait pas collectée par le garbage collector tant que tous les enfants n'ont pas été collectés. Du coup, j'ai peur que des ressources partagées (ex: traitement sur des fichiers) ne restent bloquées.

    Dans mon exemple, c'est le manager qui créé les tâches. Il y a donc pas d'interactions entre ces dernières.

    Qu'en penses-tu?
    "Winter is coming" (ma nouvelle page d'accueil)

  5. #5
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    J'ai pas tout compris

    T'as un cas d'utilisation simple, que j'y reflechisse ?

    Je dois préparer une formation pour les consultants de ma boite sur la TPL, mi janvier. Quand j'aurais un peu de temps, je la coucherai sur un article, n'hésite pas à m'y indiquer tes préoccupations, ca ne peut que m'aider

  6. #6
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    T'as un cas d'utilisation simple, que j'y reflechisse ?
    Le cas du tuto: http://immobilis.developpez.com/tuto...atches/#LIII-F
    1. Faire 1000 appels sur un service web;
    2. Enregistrer la réponse qui peut arriver après un temps d'attente de 1 à 10 secondes.
    3. Mettre le moins de temps possible.
    A+
    "Winter is coming" (ma nouvelle page d'accueil)

  7. #7
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Je comprends pas tout à fait ta problématique de flux tendu mais bon...
    Pour le coup, Task derrière c'est plus ou moins le même principe que ThreadPool...
    Quand tu fais 1000appels aléatoires, tu a pas peur que le facteur aléatoire influence les tests ? Il faudrait vérifier que le temps moyen soit identique à chaque test.

  8. #8
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    Je comprends pas tout à fait ta problématique de flux tendu mais bon...
    A mince. Du coup, tu n'est peut-être pas le seul. Je précise donc.

    Dans le tutoriel, je présente trois façons de faire du multithreading. Les deux premières sont celles qu'on trouve couramment sur internet:

    1. Tous les threads en même temps => écarté car surcharge le serveur;
    2. Une salve de threads (en utilisant le pool et un waithandle) => écarté car WaitAny ne permet pas de savoir combien de threads ont terminé (donc on ne sait pas combien on peut en relancer) et WaitAll provoque des temps morts (des threads ont fini mais le signal n'est pas envoyé car d'autres sont encore en train de travailler).

    Le terme "flux tendu" indique qu'il ne doit pas y avoir de temps mort. Grâce aux évènements, chaque thread indique quand il a fini. Le manager peut lancer un nouveau thread dès qu'un se libère. On maintient un nombre déterminé de threads en action. Cela permet de tirer parti des ressources des différents systèmes au maximum (voir la comparaison des temps).

    Est-ce plus clair?
    "Winter is coming" (ma nouvelle page d'accueil)

  9. #9
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Bonjour,

    Plusieurs point m'avez surpris à la lecture du tutoriel.
    Notamment sur les liens entre les résultats et la version suivante. Par exemple pas besoin de thread pool pour pouvoir répondre à tous les besoins.

    Mais revenons au sujet, à la lecture du cas du thread pool, je me suis posé la question suivante :
    - la découpe en "salve" de thread, comme tu dis, est elle vraiment nescessaire? n'est ce pas dans le périmètre du thread pool?

    Apres test, en local, le thread pool semble bien répondre à ce besoin, c'est à dire que tous les thread ne sont pas lancé dans la foulé. Il est possible de définir la limite soit même, Bref seul "i" thread sur les "n" tourne en même temps.

    Du coup on peut faire un "WaitAll", pour attendre la fin de tous les thread, il n'y aurra pas de temps de mort, on aurra une vrai mesure du temps total, et la possibilité d'agrégé tous les retours.

    Au niveau de TPL, il me semble que créer une class dérivé de TaskScheduler permet d'obtenir le meme comportement.
    Le principe de chainage correspond à un autre besoin. Celui qu'une tache ne doit pas être exécuté avant une autre.

  10. #10
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Ah ben là, en effet je confirme les propos de antoine.debyser, dans le cas du ThreadPool ca fait double pooling et c'est déconseillé

    Cependant, j'ai déjà eu des cas ou une librairie tierce (Oracle pour ne pas la citer) utilisait également le ThreadPool et on avait des conflits: On crééait n taches via le ThreadPool, chaque tâche ouvrait une connection base de données. Or sur Oracle, dans le driver, l'ouverture se base aussi sur le ThreadPool et l'action n'était jamais dépilée : deadlock !

  11. #11
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par antoine.debyser Voir le message
    Apres test, en local, le thread pool semble bien répondre à ce besoin, c'est à dire que tous les thread ne sont pas lancé dans la foulé. Il est possible de définir la limite soit même, Bref seul "i" thread sur les "n" tourne en même temps.
    C'est vrai mais...
    Citation Envoyé par antoine.debyser Voir le message
    Du coup on peut faire un "WaitAll", pour attendre la fin de tous les thread, il n'y aurra pas de temps de mort, on aurra une vrai mesure du temps total, et la possibilité d'agrégé tous les retours.
    Mais il me semble bien que la méthode WaitHandle.WaitAll attend en paramètre un tableau de ManualResetEvent. Or ce tableau est limité à 64 éléments. Si on veut faire 1000 appels on est coincé.

    A+
    "Winter is coming" (ma nouvelle page d'accueil)

  12. #12
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Citation Envoyé par Immobilis Voir le message
    ManualResetEvent. Or ce tableau est limité à 64 éléments. Si on veut faire 1000 appels on est coincé.
    Ca c'est bien vrai et problématique et il faut souvent ruser

  13. #13
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    Ca c'est bien vrai et problématique et il faut souvent ruser
    Sous linux vu qu'il n'y a pas de WaitMutlipleObject, on aurait utilisé un sémaphore avec un compteur

    Et sinon je viens d'y penser, mais on peut faire un Parallel.For.
    Bon ca ne se code vite et proprement que pour les cas simples.

    J'avais oublié dans mon message précédent, mais chapeau bas pour le gestionnaire de tache dans le tuto. C'est une bonne base pour les applis qui sont limité à .Net 3.5

  14. #14
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Citation Envoyé par antoine.debyser Voir le message
    Sous linux vu qu'il n'y a pas de WaitMutlipleObject, on aurait utilisé un sémaphore avec un compteur
    Ne pas oublier d'utiliser Interlocked.Increment et Interlocked.Decrement pour des opérations atomiques

  15. #15
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    Ca c'est bien vrai et problématique et il faut souvent ruser
    Je ne sais pas si on peut considérer cela comme une ruse mais avec la gestion des évènements il n'y a plus de limites.
    Citation Envoyé par antoine.debyser Voir le message
    J'avais oublié dans mon message précédent, mais chapeau bas pour le gestionnaire de tache dans le tuto. C'est une bonne base pour les applis qui sont limité à .Net 3.5
    Merci Il y a tout de même un inconvénient, c'est qu'on créé autant d'instances de thread qu'il y a de tâches. Toutefois cela ne se ressent pas sur les performances. En regardant le gestionnaire de tâches, le processus ne prend pas trop de mémoire.

    A+
    "Winter is coming" (ma nouvelle page d'accueil)

  16. #16
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    J'ai fait mumuse avec un code proche de ta solution et en y ajoutant la TPL.
    Ben pour tous, c'est grosso modo la même chose... J'ai pas fait l'étape sauvegarder dans un fichier car flemme mais ca n'a rien de bloquant.
    A chaque fois on est capable de chronométrer le temps par appel et le temps total. Ca supporte aisément plus de 64 tâches (je n'utilise pas de WaitHandle.WaitAll)
    En fait, ce qui est surtout déterminant (et en fait ce qui est complètement masqué dans ton article) c'est la capacité du client à traiter les tâches de manière concurrente. Tu remarqueras que dans mon exemple j'utilise un WCF en console pour avoir totalement la main sur le parallélisme. Toutes les requêtes sont indépendantes et une instance de service par requête.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    <serviceBehaviors>
    	<behavior name="HelloWorldBehavior">
    		<serviceThrottling maxConcurrentCalls="10"/>
    	</behavior>
    </serviceBehaviors>
    D'après moi c'est au fournisseur de brider son service en fonction de ce qu'il peut recevoir et non pas au consommateur. Du coup, y'a pas vraiment de raisons de brider côté client. Et quand bien même tu voudrais brider, tu pourrais totalement avec le sémaphore.
    Fichiers attachés Fichiers attachés

  17. #17
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Erf, je reviens sur ce que j'ai dit. Quand on utilise des Thread à gogo ca fait planter le client car ca utilise trop de connections
    Problème qu'on ne rencontre pas avec des pools de threads (ThreadPool et TPL)
    Edit: Il suffit de placer un sémaphore autour de l'appel et ca fonctionne

  18. #18
    Membre éprouvé Avatar de sisqo60
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Février 2006
    Messages
    754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 754
    Points : 1 188
    Points
    1 188
    Par défaut
    Bonjour,

    Je trouve ton article très utile lorsqu'on développe sur des versions de framework antérieures au 4. Comme l'a très bien mis en évidence Nathanael Marchand, TPL est apparu avec le framework 4 qui a "simplifié" l'utilisation des threads, mais depuis le framework 4.5, il y a 2 nouveaux mots clés du langage, async et await, qui simplifient vraiment l'utilisation des threads contrairement à TPL.

    Pour avoir passé du temps à jouer avec les thread, threadpool, backgroundworker et être passé à TPL puis à async programming, je trouve que les dév de Microsoft ont trouvé une très bonne solution pour simplifier l'utilisation des threads.

    L'avez-vous testé?
    Un âne se croit savant parce qu'on le charge de livres (proverbe américain)

    N'oubliez pas de avant de
    Pas de question techniques par MP, c'est contre la philosophie du forum

  19. #19
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 080
    Points
    8 080
    Par défaut
    Citation Envoyé par sisqo60 Voir le message
    Pour avoir passé du temps à jouer avec les thread, threadpool, backgroundworker et être passé à TPL puis à async programming, je trouve que les dév de Microsoft ont trouvé une très bonne solution pour simplifier l'utilisation des threads.
    async/await c'est très bien pour les cas simples d'asynchronisme. Mais on tombe très vite dans des pièges comme celui ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foreach(var toto in titi)
    {
        var tutu = await toto.DoAsync();
    }
    Le code ci dessus s'executera séquentiellement (un toto après l'autre) et non pas les toto en parallèle.
    Car en effet, async/await gèrent l'asynchronisme, pas le parallélisme (différence fondamentale)

  20. #20
    Expert éminent
    Avatar de Immobilis
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2004
    Messages
    6 559
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 559
    Points : 9 506
    Points
    9 506
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    J'ai fait mumuse avec un code proche de ta solution et en y ajoutant la TPL.
    Tu t'es bien amusé je vois ça C'est très intéressant

    La méthode "E" est très chouette! Elle me semble la plus adaptée à mon besoin, car il me semble qu'elle permet de traiter les tâches de manière vraiment indépendante. La méthode Task.WaitAll(tasks.ToArray()) est aussi beaucoup plus élégante que mon PauseAndWait

    Les méthodes B et C partagent le compteur "c" ainsi que le "ManualResetEvent". Cela créé une certaine dépendance. Je suis un peu réservé du coup. Pour ceux qui voudraient utiliser ces méthodes, ne faudrait-il pas mettre ce bloc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if (Interlocked.Decrement(ref c) == 0)
    {
    	wh.Set();
    }
    dans un try{ }finally{ } pour être certain de décrémenter le compteur. Dans le cas contraire, à moins de mettre un timeout, on attendra indéfiniment, non?

    Dans la méthode "F", tu utilises le même client. Le Runtime utilise la même instance à l'exécution?

    A+
    "Winter is coming" (ma nouvelle page d'accueil)

Discussions similaires

  1. Réponses: 33
    Dernier message: 25/11/2011, 14h37
  2. Réponses: 6
    Dernier message: 12/04/2007, 23h16

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