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

C++ Discussion :

[C++] Synchroniser deux threads


Sujet :

C++

  1. #1
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut [C++] Synchroniser deux threads
    Bonsoir,

    J'utilise la librairie pthread pour créer mes threads et je suis face à un problème de synchronisation :
    Je travaille sur la recherche bidirectionnelle dans un graphe.

    J'ai donc créer une fonction qui fait une recherche de l'état initial vers l'état final (forward) et une autre qui fait dans l'autre sens (backward)

    Les étapes de l'algorithme sont les suivants :
    1. Tant que la solution n'est pas trouvée :
    2. Récupérer le résultat de la recherche FORWARD
    3. Récupérer le résultat de la recherche BACKWARD
    4. Faire un test d'union entre les deux recherches
    5. Si ce test est concluant alors quitter l'algo sinon continuer


    En séquentiel, ca marche très bien mais je n'arrive pas à paralléliser la recherche BACKWARD (la recherche FORWARD sera faite dans le thread principal).

    L'idée serait :
    • Créer le thread de recherche pour BACKWARD (qui sera en boucle infinie)
    • Tant que la solution n'est pas trouvée : (thread principal)
    • Récupérer le résultat de la recherche FORWARD (thread principal)
    • Attendre que le thread ait fini le premier tour
    • Faire un test d'union entre les deux recherches (thread principal)
    • Si ce test est concluant alors quitter l'algo sinon continuer (thread principal)


    Je dois utiliser les threads avec conditions (et peut être des mutex) mais voilà, je sèche.

    A mon avis, c'est pas clair car ce n'est pas clar pour moi non plus

    Avez vous une idée ?

    Merci d'avance
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  2. #2
    Invité
    Invité(e)
    Par défaut
    Salut
    A priori une recherche ne modifie pas les données que l'on parcourt donc tu ne devrais pas avoir besoin de mutex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void rechercheBackward(const Graphe & graphe, Resultat & resultat) { ... }
    void rechercheForward(const Graphe & graphe, Resultat & resultat) { ... }
    ...
    // recherches backward et forward en parallele
    std::thread threadBackward ( rechercheBackward, std::cref(monGraphe), std::ref(resultatBackward) );
    rechercheForward(monGraphe, resultatForward);
    threadBackward.join();
    // union des 2 recherches
    ...
    http://en.cppreference.com/w/cpp/thread/thread/thread
    Si tu veux absolument utiliser la lib pthread, le code devrait être assez similaire.

  3. #3
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::thread threadBackward ( rechercheBackward, std::cref(monGraphe), std::ref(resultatBackward) );
    Avec std::thread, j'aime bien passer par une lambda du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::thread threadBackward([&]() { rechercheBackward(monGraphe, resultatBackward); });
    Et dans notre cas, une fonction qui retourne quelque chose est légitime (resultatBackward).
    Donc avec une fonction de ce genre : return_t backward(graph_t const &), on peut utiliser une std::future :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    auto f = std::async(std::launch::deferred, [&]() -> return_t { return resultatBackward(monGraphe); });
    // ...
    auto resultatBackward = f.get() // Pour récupérer le résultat (faire un seul appel à .get() car le résultat peut être déplacé)
    Edit : nokomprendo > oui, il manque bien le return (erreur copier / coller)

  4. #4
    Invité
    Invité(e)
    Par défaut
    Oui tu as raison mais comme il était parti sur du pthread, je n'ai pas voulu trop le brusquer...

    Pour la lambda, personnellement je préfère expliciter la fermeture (surtout s'il y a des références) et éviter le type de retour s'il peut être déduit; et je crois qu'il manque un return :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    auto f = std::async(std::launch::deferred, [&monGraphe]() { return resultatBackward(monGraphe); });
    // ...
    Par contre, je ne suis pas fan de la lambda ici car il y doit y avoir un (léger) surcoût pour la créer et de plus on ne peut pas spécifier la référence capturée en const (ou du moins, je ne sais pas comment faire). Donc, je préfère vraiment :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    auto f = std::async(std::launch::deferred, resultatBackward, std::cref(monGraphe) );
    // ...

  5. #5
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par nokomprendo Voir le message
    Salut
    A priori une recherche ne modifie pas les données que l'on parcourt donc tu ne devrais pas avoir besoin de mutex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void rechercheBackward(const Graphe & graphe, Resultat & resultat) { ... }
    void rechercheForward(const Graphe & graphe, Resultat & resultat) { ... }
    ...
    // recherches backward et forward en parallele
    std::thread threadBackward ( rechercheBackward, std::cref(monGraphe), std::ref(resultatBackward) );
    rechercheForward(monGraphe, resultatForward);
    threadBackward.join();
    // union des 2 recherches
    ...
    http://en.cppreference.com/w/cpp/thread/thread/thread
    Si tu veux absolument utiliser la lib pthread, le code devrait être assez similaire.
    Bonjour,

    Je n'utilise pas std::thread car mon compilateur ne la gère pas c'est pour ca que je souhaite passer par pthread. (et puis je ne suis pas très à l'aise avec les lambda )

    J'ai déjà essayé avec pthread ton approche mais ca n'a pas marché, certainement parce que je m'y suis mal pris

    Soit ma fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void rechercheBackward(...) {  
        while (!solved_backward && !backwardStates.empty())
        {  
            // ...
        }
    }
    void rechercheForward(...) {  
        while (!solved_forward && !forwardStates.empty())
        {  
            // ...
        }
    }
    Et celui qui appelle les fonctions :
    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
     
    int main()
    {
          // création du thread avec pthread qui lance rechercheBackward(...) en parralèle
         Thread thread = pthread_create(...)
         while (!solved_forward && !forwardStates.empty()
                && !solved_backward && !backwardStates.empty())
         {
               rechercheForward(....);
     
               // Attente du thread rechercheBackward()
               pthread_join(&thread, NULL);
     
                // Union des recherches
         }
    }
    Le problème est que pthread_join() attend que le thread rechercheBackward() soit quitté et vu qu'il est en boucle infinie... mon thread principal reste bloqué... A mon avis, il faut utiliser pthread_cond_wait() et pthread_cond_signal() mais je n'y arrive pas.

    Voilà un "pseudo" code de la version séquentielle qui marche très bien (tous les cas en sont pas gérés, c'est pour simplifier le code)
    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
    bool AlgoBidirect::start()
    {
        bool overlapped = false;
        Node* current_fw = NULL, *current_rv=NULL;
        std::vector<Node*> succ, pred , nodes;
        std::queue<Node*> forwardStates, backwardStates;
     
        // Insert initial nodes in queue
        forwardStates.push(/* initial node */);
        backwardStates.push(/* initial node */);
     
        while (!solved_forward && !forwardStates.empty()
                && !solved_backward && !backwardStates.empty()
    			)
        {
            // Forward
            {
                // Get first node in frontier
                current_fw = forwardStates.front(); forwardStates.pop();
     
                // Get successors and check if current_fw is a goal state
                succ = this->m_forward->_checkNode(current_fw, this->m_solved_forward);
                for (const auto& n : succ)
                    forwardStates.push(n);
            }
     
            // Backward
            {
                current_rv = backwardStates.front(); backwardStates.pop();
     
                // Get successors and check if current_rv is a goal state
                pred = this->m_backward->checkNode(current_rv, this->m_solved_backward);
                for (const auto& n : pred)
                    backwardStates.push(n);
            }
     
            // Find overlapping between forward and reverse
    		{
                overlapped = checkMergeBetweenFwAndBw(...);
    		}
        }
    	// ici on a trouvé si overlapped = true
    }
    Merci
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  6. #6
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par nokomprendo Voir le message
    Pour la lambda [...] et je crois qu'il manque un return :
    Merci, c'est corrigé.

    Citation Envoyé par nokomprendo Voir le message
    Pour la lambda, personnellement je préfère expliciter la fermeture
    Dans le cas général je n'ai pas trop d'avis là dessus. Mais ici on se sert de la lambda comme on aurait pu utiliser std::bind ou un simple foncteur. Plus le code de la lambda sera court plus il sera facile de voir que l'on cherche à transformer un appel de fonction à N arguments en un appel de fonction sans argument. Donc dans ce cas, j'ai une petite préférence à ne pas expliciter la liste de capture ainsi que le type de retour.

    Citation Envoyé par nokomprendo Voir le message
    Par contre, je ne suis pas fan de la lambda ici car il y doit y avoir un (léger) surcoût pour la créer et de plus on ne peut pas spécifier la référence capturée en const (ou du moins, je ne sais pas comment faire). Donc, je préfère vraiment :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    auto f = std::async(std::launch::deferred, resultatBackward, std::cref(monGraphe) );
    // ...
    S'il y a un surcoût dû à la lambda, il faut changer de compilateur. Les lambdas sont forcément inline et devraient être inliné.
    J'aime bien la lambda car elle permet de ne pas oublier les std::ref ou std::cref et quelle s'adapte automatiquement si ces "détails" changent.
    Oui, on ne peut capturer que par copie ou référence. Mais rien n'empêche d'avoir un code proche de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::async(std::launch::deferred, [&]() { return resultatBackward(fonction_qui_prend_un_T_ref_et_qui_retourne_un_T_const_ref(monGraphe)); });

  7. #7
    Invité
    Invité(e)
    Par défaut
    @Ehonn: je ne suis pas expert en compilation mais il me semble qu'une lambda n'est qu'un raccourci syntaxique pour un objet-fonction donc cela implique effectivement un surcôut dans la taille de l'exécutable alors que ce n'est pas le cas si on utilise un bind ou directement la fonction.
    Concernant les std::ref et std::cref, il ne faut effectivement pas les oublier mais ils permettent d'éviter des erreurs genre "modifier une donnée qui devrait être const mais que la lambda a adapté automatiquement".
    Ceci dit, tout çà c'est du détail, les deux solutions fonctionnent bien.

    @Aspic: si je comprends bien ton problème, tu veux chercher dans un graphe dans deux sens en même temps, tout arrêter quand tu as trouvé et tout çà en évitant de traiter des noeuds déjà traités (overlapping) ?
    À mon avis, une solution simple est que les deux recherches s'observent mais sans accès "concurrent" (lecture-écriture et non écriture-écriture) :
    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
     
    bool foundForward = false;
    bool foundBackward = false;
     
    void rechercheForward(...) {
    	// rechercheForward modifie les donnees forward et observe les donnees backward
    	while (not (foundForward or foundBackward or forwardStates.empty()) {
    		// mettre à jour forwardStates
    		// mettre à jour foundForward si on a trouve
    	}
    }
     
    void rechercheBackward(...) {
    	// rechercheBackward modifie les donnees backward et observe les donnees forward
    	while (not (foundBackward or foundForward or backwardStates.empty()) {
    		// mettre à jour backwardStates
    		// mettre à jour foundBackward si on a trouve
    	}
    }
     
    int main() {
    	std::thread t(rechercheBackward...);
    	rechercheForward(...);
    	t.join(); 
    	if (foundForward)
    		// récupérer le résultat de la recherche forward
    	else if (foundBackward)
    		// récupérer le résultat de la recherche backward
    	else
    		// non trouvé
    }
    Cet exemple ne traite pas l'overlapping mais avec une variable partagée + mutex ce ne doit pas être difficile.
    Concernant ton problème de std::thread, çà m'étonne car c'est juste une surcouche (souvent à pthread justement).
    Bref, j'espère que tout çà t'aidera et désolé si ce n'est pas la meilleure solution/implémentation ou si j'ai juste rien compris à ton problème.
    Dernière modification par Invité ; 08/11/2015 à 16h03.

  8. #8
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par nokomprendo Voir le message
    @Ehonn: je ne suis pas expert en compilation mais il me semble qu'une lambda n'est qu'un raccourci syntaxique pour un objet-fonction donc cela implique effectivement un surcôut dans la taille de l'exécutable alors que ce n'est pas le cas si on utilise un bind ou directement la fonction.
    std::bind retourne lui aussi un foncteur.
    Le compilateur inline. Testons cela sur un exemple simple.

    Pour ce code :
    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
    #include <iostream>
     
    class test_t
    {
    public:
    	void operator()() const { std::cout << "Test" << std::endl; }
    };
     
    int main()
    {
    	test_t fct;
    	fct();
     
    	return 0;
    }
    Voici la sortie assembleur avec GCC en O0 :
    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
            .file   "main.cpp"
            .section        .rodata
            .type   _ZStL19piecewise_construct, @object
            .size   _ZStL19piecewise_construct, 1
    _ZStL19piecewise_construct:
            .zero   1
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
    .LC0:
            .string "Test"
            .section        .text._ZNK6test_tclEv,"axG",@progbits,_ZNK6test_tclEv,comdat
            .align 2
            .weak   _ZNK6test_tclEv
            .type   _ZNK6test_tclEv, @function
    _ZNK6test_tclEv:
    .LFB1250:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movq    %rdi, -8(%rbp)
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
            movq    %rax, %rdi
            call    _ZNSolsEPFRSoS_E
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1250:
            .size   _ZNK6test_tclEv, .-_ZNK6test_tclEv
            .text
            .globl  main
            .type   main, @function
    main:
    .LFB1251:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            leaq    -1(%rbp), %rax
            movq    %rax, %rdi
            call    _ZNK6test_tclEv
            movl    $0, %eax
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1251:
            .size   main, .-main
            .type   _Z41__static_initialization_and_destruction_0ii, @function
    _Z41__static_initialization_and_destruction_0ii:
    .LFB1406:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movl    %edi, -4(%rbp)
            movl    %esi, -8(%rbp)
            cmpl    $1, -4(%rbp)
            jne     .L4
            cmpl    $65535, -8(%rbp)
            jne     .L4
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            call    __cxa_atexit
    .L4:
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1406:
            .size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1407:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            movl    $65535, %esi
            movl    $1, %edi
            call    _Z41__static_initialization_and_destruction_0ii
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1407:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et en O3 :
    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
            .file   "main.cpp"
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC0:
            .string "Test"
            .section        .text.unlikely,"ax",@progbits
    .LCOLDB1:
            .section        .text.startup,"ax",@progbits
    .LHOTB1:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1275:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
            xorl    %eax, %eax
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            ret
            .cfi_endproc
    .LFE1275:
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE1:
            .section        .text.startup
    .LHOTE1:
            .section        .text.unlikely
    .LCOLDB2:
            .section        .text.startup
    .LHOTB2:
            .p2align 4,,15
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1431:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1431:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .text.unlikely
    .LCOLDE2:
            .section        .text.startup
    .LHOTE2:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et maintenant avec une lambda :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <iostream>
     
    int main()
    {
    	auto fct = []() { std::cout << "Test" << std::endl; };
    	fct();
     
    	return 0;
    }
    En O0 :
    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
            .file   "main.cpp"
            .section        .rodata
            .type   _ZStL19piecewise_construct, @object
            .size   _ZStL19piecewise_construct, 1
    _ZStL19piecewise_construct:
            .zero   1
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
    .LC0:
            .string "Test"
            .text
            .align 2
            .type   _ZZ4mainENKUlvE_clEv, @function
    _ZZ4mainENKUlvE_clEv:
    .LFB1251:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movq    %rdi, -8(%rbp)
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
            movq    %rax, %rdi
            call    _ZNSolsEPFRSoS_E
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1251:
            .size   _ZZ4mainENKUlvE_clEv, .-_ZZ4mainENKUlvE_clEv
            .globl  main
            .type   main, @function
    main:
    .LFB1250:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            leaq    -1(%rbp), %rax
            movq    %rax, %rdi
            call    _ZZ4mainENKUlvE_clEv
            movl    $0, %eax
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1250:
            .size   main, .-main
            .type   _Z41__static_initialization_and_destruction_0ii, @function
    _Z41__static_initialization_and_destruction_0ii:
    .LFB1408:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movl    %edi, -4(%rbp)
            movl    %esi, -8(%rbp)
            cmpl    $1, -4(%rbp)
            jne     .L4
            cmpl    $65535, -8(%rbp)
            jne     .L4
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            call    __cxa_atexit
    .L4:
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1408:
            .size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1409:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            movl    $65535, %esi
            movl    $1, %edi
            call    _Z41__static_initialization_and_destruction_0ii
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1409:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et en O3 :
    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
            .file   "main.cpp"
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC0:
            .string "Test"
            .section        .text.unlikely,"ax",@progbits
    .LCOLDB1:
            .section        .text.startup,"ax",@progbits
    .LHOTB1:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1274:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
            xorl    %eax, %eax
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            ret
            .cfi_endproc
    .LFE1274:
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE1:
            .section        .text.startup
    .LHOTE1:
            .section        .text.unlikely
    .LCOLDB2:
            .section        .text.startup
    .LHOTB2:
            .p2align 4,,15
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1433:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1433:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .text.unlikely
    .LCOLDE2:
            .section        .text.startup
    .LHOTE2:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et l'appel à la fonction directement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>
     
    int main()
    {
    	std::cout << "Test" << std::endl;
     
    	return 0;
    }
    En O0 :
    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
            .file   "main.cpp"
            .section        .rodata
            .type   _ZStL19piecewise_construct, @object
            .size   _ZStL19piecewise_construct, 1
    _ZStL19piecewise_construct:
            .zero   1
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
    .LC0:
            .string "Test"
            .text
            .globl  main
            .type   main, @function
    main:
    .LFB1250:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
            movq    %rax, %rdi
            call    _ZNSolsEPFRSoS_E
            movl    $0, %eax
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1250:
            .size   main, .-main
            .type   _Z41__static_initialization_and_destruction_0ii, @function
    _Z41__static_initialization_and_destruction_0ii:
    .LFB1405:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            subq    $16, %rsp
            movl    %edi, -4(%rbp)
            movl    %esi, -8(%rbp)
            cmpl    $1, -4(%rbp)
            jne     .L3
            cmpl    $65535, -8(%rbp)
            jne     .L3
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            call    __cxa_atexit
    .L3:
            leave
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1405:
            .size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1406:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            movl    $65535, %esi
            movl    $1, %edi
            call    _Z41__static_initialization_and_destruction_0ii
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE1406:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et en 03 :
    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
            .file   "main.cpp"
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC0:
            .string "Test"
            .section        .text.unlikely,"ax",@progbits
    .LCOLDB1:
            .section        .text.startup,"ax",@progbits
    .LHOTB1:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1274:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $.LC0, %esi
            movl    $_ZSt4cout, %edi
            call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
            xorl    %eax, %eax
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            ret
            .cfi_endproc
    .LFE1274:
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE1:
            .section        .text.startup
    .LHOTE1:
            .section        .text.unlikely
    .LCOLDB2:
            .section        .text.startup
    .LHOTB2:
            .p2align 4,,15
            .type   _GLOBAL__sub_I_main, @function
    _GLOBAL__sub_I_main:
    .LFB1430:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1430:
            .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
            .section        .text.unlikely
    .LCOLDE2:
            .section        .text.startup
    .LHOTE2:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I_main
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Maintenant testons avec une capture :
    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
    #include <iostream>
    #include <string>
    #include <functional>
     
    void fct(std::string & s) { s += "_modifié"; }
     
    int main()
    {
    	std::string s = "string";
     
    	fct(s);
     
    	std::cout << s << std::endl;
     
    	return 0;
    }
    En O3 :
    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
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
            .file   "main.cpp"
            .section        .text.unlikely,"ax",@progbits
            .align 2
    .LCOLDB0:
            .text
    .LHOTB0:
            .align 2
            .p2align 4,,15
            .type   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, @function
    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0:
    .LFB1778:
            .cfi_startproc
            movl    $_ZL28__gthrw___pthread_key_createPjPFvPvE, %eax
            testq   %rax, %rax
            je      .L2
            movl    $-1, %eax
            lock xaddl      %eax, 16(%rdi)
    .L3:
            testl   %eax, %eax
            jle     .L6
            rep ret
    .L2:
            movl    16(%rdi), %eax
            leal    -1(%rax), %edx
            movl    %edx, 16(%rdi)
            jmp     .L3
    .L6:
            jmp     _ZNSs4_Rep10_M_destroyERKSaIcE
            .cfi_endproc
    .LFE1778:
            .size   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, .-_ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            .section        .text.unlikely
    .LCOLDE0:
            .text
    .LHOTE0:
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC1:
            .string "_modifi\303\251"
            .section        .text.unlikely
    .LCOLDB2:
            .text
    .LHOTB2:
            .p2align 4,,15
            .globl  _Z3fctRSs
            .type   _Z3fctRSs, @function
    _Z3fctRSs:
    .LFB1619:
            .cfi_startproc
            movl    $9, %edx
            movl    $.LC1, %esi
            jmp     _ZNSs6appendEPKcm
            .cfi_endproc
    .LFE1619:
            .size   _Z3fctRSs, .-_Z3fctRSs
            .section        .text.unlikely
    .LCOLDE2:
            .text
    .LHOTE2:
            .section        .rodata.str1.1
    .LC3:
            .string "string"
            .section        .text.unlikely
    .LCOLDB4:
            .section        .text.startup,"ax",@progbits
    .LHOTB4:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1620:
            .cfi_startproc
            .cfi_personality 0x3,__gxx_personality_v0
            .cfi_lsda 0x3,.LLSDA1620
            pushq   %rbx
            .cfi_def_cfa_offset 16
            .cfi_offset 3, -16
            movl    $.LC3, %esi
            subq    $32, %rsp
            .cfi_def_cfa_offset 48
            leaq    16(%rsp), %rdi
            leaq    15(%rsp), %rdx
    .LEHB0:
            call    _ZNSsC1EPKcRKSaIcE
    .LEHE0:
            leaq    16(%rsp), %rdi
            movl    $.LC1, %esi
    .LEHB1:
            call    _ZNSs6appendEPKc
            movq    16(%rsp), %rsi
            movl    $_ZSt4cout, %edi
            movq    -24(%rsi), %rdx
            call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    .LEHE1:
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            jne     .L21
    .L15:
            addq    $32, %rsp
            .cfi_remember_state
            .cfi_def_cfa_offset 16
            xorl    %eax, %eax
            popq    %rbx
            .cfi_def_cfa_offset 8
            ret
    .L21:
            .cfi_restore_state
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            jmp     .L15
    .L12:
            movq    %rax, %rbx
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            je      .L11
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
    .L11:
            movq    %rbx, %rdi
    .LEHB2:
            call    _Unwind_Resume
    .LEHE2:
            .cfi_endproc
    .LFE1620:
            .globl  __gxx_personality_v0
            .section        .gcc_except_table,"a",@progbits
    .LLSDA1620:
            .byte   0xff
            .byte   0xff
            .byte   0x1
            .uleb128 .LLSDACSE1620-.LLSDACSB1620
    .LLSDACSB1620:
            .uleb128 .LEHB0-.LFB1620
            .uleb128 .LEHE0-.LEHB0
            .uleb128 0
            .uleb128 0
            .uleb128 .LEHB1-.LFB1620
            .uleb128 .LEHE1-.LEHB1
            .uleb128 .L12-.LFB1620
            .uleb128 0
            .uleb128 .LEHB2-.LFB1620
            .uleb128 .LEHE2-.LEHB2
            .uleb128 0
            .uleb128 0
    .LLSDACSE1620:
            .section        .text.startup
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE4:
            .section        .text.startup
    .LHOTE4:
            .section        .text.unlikely
    .LCOLDB5:
            .section        .text.startup
    .LHOTB5:
            .p2align 4,,15
            .type   _GLOBAL__sub_I__Z3fctRSs, @function
    _GLOBAL__sub_I__Z3fctRSs:
    .LFB1777:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1777:
            .size   _GLOBAL__sub_I__Z3fctRSs, .-_GLOBAL__sub_I__Z3fctRSs
            .section        .text.unlikely
    .LCOLDE5:
            .section        .text.startup
    .LHOTE5:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I__Z3fctRSs
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .weakref        _ZL28__gthrw___pthread_key_createPjPFvPvE,__pthread_key_create
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Avec une lambda :
    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
    #include <iostream>
    #include <string>
    #include <functional>
     
    void fct(std::string & s) { s += "_modifié"; }
     
    int main()
    {
    	std::string s = "string";
     
    	auto f = [&]() { fct(s); }; f();
     
    	std::cout << s << std::endl;
     
    	return 0;
    }
    En O3 :
    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
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
            .file   "main.cpp"
            .section        .text.unlikely,"ax",@progbits
            .align 2
    .LCOLDB0:
            .text
    .LHOTB0:
            .align 2
            .p2align 4,,15
            .type   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, @function
    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0:
    .LFB1779:
            .cfi_startproc
            movl    $_ZL28__gthrw___pthread_key_createPjPFvPvE, %eax
            testq   %rax, %rax
            je      .L2
            movl    $-1, %eax
            lock xaddl      %eax, 16(%rdi)
    .L3:
            testl   %eax, %eax
            jle     .L6
            rep ret
    .L2:
            movl    16(%rdi), %eax
            leal    -1(%rax), %edx
            movl    %edx, 16(%rdi)
            jmp     .L3
    .L6:
            jmp     _ZNSs4_Rep10_M_destroyERKSaIcE
            .cfi_endproc
    .LFE1779:
            .size   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, .-_ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            .section        .text.unlikely
    .LCOLDE0:
            .text
    .LHOTE0:
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC1:
            .string "_modifi\303\251"
            .section        .text.unlikely
    .LCOLDB2:
            .text
    .LHOTB2:
            .p2align 4,,15
            .globl  _Z3fctRSs
            .type   _Z3fctRSs, @function
    _Z3fctRSs:
    .LFB1619:
            .cfi_startproc
            movl    $9, %edx
            movl    $.LC1, %esi
            jmp     _ZNSs6appendEPKcm
            .cfi_endproc
    .LFE1619:
            .size   _Z3fctRSs, .-_Z3fctRSs
            .section        .text.unlikely
    .LCOLDE2:
            .text
    .LHOTE2:
            .section        .rodata.str1.1
    .LC3:
            .string "string"
            .section        .text.unlikely
    .LCOLDB4:
            .section        .text.startup,"ax",@progbits
    .LHOTB4:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1620:
            .cfi_startproc
            .cfi_personality 0x3,__gxx_personality_v0
            .cfi_lsda 0x3,.LLSDA1620
            pushq   %rbx
            .cfi_def_cfa_offset 16
            .cfi_offset 3, -16
            movl    $.LC3, %esi
            subq    $32, %rsp
            .cfi_def_cfa_offset 48
            leaq    16(%rsp), %rdi
            leaq    15(%rsp), %rdx
    .LEHB0:
            call    _ZNSsC1EPKcRKSaIcE
    .LEHE0:
            leaq    16(%rsp), %rdi
            movl    $.LC1, %esi
    .LEHB1:
            call    _ZNSs6appendEPKc
            movq    16(%rsp), %rsi
            movl    $_ZSt4cout, %edi
            movq    -24(%rsi), %rdx
            call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    .LEHE1:
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            jne     .L21
    .L15:
            addq    $32, %rsp
            .cfi_remember_state
            .cfi_def_cfa_offset 16
            xorl    %eax, %eax
            popq    %rbx
            .cfi_def_cfa_offset 8
            ret
    .L21:
            .cfi_restore_state
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            jmp     .L15
    .L12:
            movq    %rax, %rbx
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            je      .L11
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
    .L11:
            movq    %rbx, %rdi
    .LEHB2:
            call    _Unwind_Resume
    .LEHE2:
            .cfi_endproc
    .LFE1620:
            .globl  __gxx_personality_v0
            .section        .gcc_except_table,"a",@progbits
    .LLSDA1620:
            .byte   0xff
            .byte   0xff
            .byte   0x1
            .uleb128 .LLSDACSE1620-.LLSDACSB1620
    .LLSDACSB1620:
            .uleb128 .LEHB0-.LFB1620
            .uleb128 .LEHE0-.LEHB0
            .uleb128 0
            .uleb128 0
            .uleb128 .LEHB1-.LFB1620
            .uleb128 .LEHE1-.LEHB1
            .uleb128 .L12-.LFB1620
            .uleb128 0
            .uleb128 .LEHB2-.LFB1620
            .uleb128 .LEHE2-.LEHB2
            .uleb128 0
            .uleb128 0
    .LLSDACSE1620:
            .section        .text.startup
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE4:
            .section        .text.startup
    .LHOTE4:
            .section        .text.unlikely
    .LCOLDB5:
            .section        .text.startup
    .LHOTB5:
            .p2align 4,,15
            .type   _GLOBAL__sub_I__Z3fctRSs, @function
    _GLOBAL__sub_I__Z3fctRSs:
    .LFB1778:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1778:
            .size   _GLOBAL__sub_I__Z3fctRSs, .-_GLOBAL__sub_I__Z3fctRSs
            .section        .text.unlikely
    .LCOLDE5:
            .section        .text.startup
    .LHOTE5:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I__Z3fctRSs
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .weakref        _ZL28__gthrw___pthread_key_createPjPFvPvE,__pthread_key_create
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits
    Et avec std::bind :
    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
    #include <iostream>
    #include <string>
    #include <functional>
     
    void fct(std::string & s) { s += "_modifié"; }
     
    int main()
    {
    	std::string s = "string";
     
    	auto f = std::bind(fct, std::ref(s)); f();
     
    	std::cout << s << std::endl;
     
    	return 0;
    }
    En O3 :
    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
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
            .file   "main.cpp"
            .section        .text.unlikely,"ax",@progbits
            .align 2
    .LCOLDB0:
            .text
    .LHOTB0:
            .align 2
            .p2align 4,,15
            .type   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, @function
    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0:
    .LFB1848:
            .cfi_startproc
            movl    $_ZL28__gthrw___pthread_key_createPjPFvPvE, %eax
            testq   %rax, %rax
            je      .L2
            movl    $-1, %eax
            lock xaddl      %eax, 16(%rdi)
    .L3:
            testl   %eax, %eax
            jle     .L6
            rep ret
    .L2:
            movl    16(%rdi), %eax
            leal    -1(%rax), %edx
            movl    %edx, 16(%rdi)
            jmp     .L3
    .L6:
            jmp     _ZNSs4_Rep10_M_destroyERKSaIcE
            .cfi_endproc
    .LFE1848:
            .size   _ZNSs4_Rep10_M_disposeERKSaIcE.part.0, .-_ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            .section        .text.unlikely
    .LCOLDE0:
            .text
    .LHOTE0:
            .section        .rodata.str1.1,"aMS",@progbits,1
    .LC1:
            .string "_modifi\303\251"
            .section        .text.unlikely
    .LCOLDB2:
            .text
    .LHOTB2:
            .p2align 4,,15
            .globl  _Z3fctRSs
            .type   _Z3fctRSs, @function
    _Z3fctRSs:
    .LFB1619:
            .cfi_startproc
            movl    $9, %edx
            movl    $.LC1, %esi
            jmp     _ZNSs6appendEPKcm
            .cfi_endproc
    .LFE1619:
            .size   _Z3fctRSs, .-_Z3fctRSs
            .section        .text.unlikely
    .LCOLDE2:
            .text
    .LHOTE2:
            .section        .rodata.str1.1
    .LC3:
            .string "string"
            .section        .text.unlikely
    .LCOLDB4:
            .section        .text.startup,"ax",@progbits
    .LHOTB4:
            .p2align 4,,15
            .globl  main
            .type   main, @function
    main:
    .LFB1620:
            .cfi_startproc
            .cfi_personality 0x3,__gxx_personality_v0
            .cfi_lsda 0x3,.LLSDA1620
            pushq   %rbx
            .cfi_def_cfa_offset 16
            .cfi_offset 3, -16
            movl    $.LC3, %esi
            subq    $32, %rsp
            .cfi_def_cfa_offset 48
            leaq    16(%rsp), %rdi
            leaq    15(%rsp), %rdx
    .LEHB0:
            call    _ZNSsC1EPKcRKSaIcE
    .LEHE0:
            leaq    16(%rsp), %rdi
    .LEHB1:
            call    _Z3fctRSs
            movq    16(%rsp), %rsi
            movl    $_ZSt4cout, %edi
            movq    -24(%rsi), %rdx
            call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
            movq    %rax, %rdi
            call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    .LEHE1:
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            jne     .L21
    .L15:
            addq    $32, %rsp
            .cfi_remember_state
            .cfi_def_cfa_offset 16
            xorl    %eax, %eax
            popq    %rbx
            .cfi_def_cfa_offset 8
            ret
    .L21:
            .cfi_restore_state
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
            jmp     .L15
    .L12:
            movq    %rax, %rbx
            movq    16(%rsp), %rax
            leaq    -24(%rax), %rdi
            cmpq    $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
            je      .L11
            leaq    15(%rsp), %rsi
            call    _ZNSs4_Rep10_M_disposeERKSaIcE.part.0
    .L11:
            movq    %rbx, %rdi
    .LEHB2:
            call    _Unwind_Resume
    .LEHE2:
            .cfi_endproc
    .LFE1620:
            .globl  __gxx_personality_v0
            .section        .gcc_except_table,"a",@progbits
    .LLSDA1620:
            .byte   0xff
            .byte   0xff
            .byte   0x1
            .uleb128 .LLSDACSE1620-.LLSDACSB1620
    .LLSDACSB1620:
            .uleb128 .LEHB0-.LFB1620
            .uleb128 .LEHE0-.LEHB0
            .uleb128 0
            .uleb128 0
            .uleb128 .LEHB1-.LFB1620
            .uleb128 .LEHE1-.LEHB1
            .uleb128 .L12-.LFB1620
            .uleb128 0
            .uleb128 .LEHB2-.LFB1620
            .uleb128 .LEHE2-.LEHB2
            .uleb128 0
            .uleb128 0
    .LLSDACSE1620:
            .section        .text.startup
            .size   main, .-main
            .section        .text.unlikely
    .LCOLDE4:
            .section        .text.startup
    .LHOTE4:
            .section        .text.unlikely
    .LCOLDB5:
            .section        .text.startup
    .LHOTB5:
            .p2align 4,,15
            .type   _GLOBAL__sub_I__Z3fctRSs, @function
    _GLOBAL__sub_I__Z3fctRSs:
    .LFB1847:
            .cfi_startproc
            subq    $8, %rsp
            .cfi_def_cfa_offset 16
            movl    $_ZStL8__ioinit, %edi
            call    _ZNSt8ios_base4InitC1Ev
            movl    $__dso_handle, %edx
            movl    $_ZStL8__ioinit, %esi
            movl    $_ZNSt8ios_base4InitD1Ev, %edi
            addq    $8, %rsp
            .cfi_def_cfa_offset 8
            jmp     __cxa_atexit
            .cfi_endproc
    .LFE1847:
            .size   _GLOBAL__sub_I__Z3fctRSs, .-_GLOBAL__sub_I__Z3fctRSs
            .section        .text.unlikely
    .LCOLDE5:
            .section        .text.startup
    .LHOTE5:
            .section        .init_array,"aw"
            .align 8
            .quad   _GLOBAL__sub_I__Z3fctRSs
            .local  _ZStL8__ioinit
            .comm   _ZStL8__ioinit,1,1
            .weakref        _ZL28__gthrw___pthread_key_createPjPFvPvE,__pthread_key_create
            .hidden __dso_handle
            .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
            .section        .note.GNU-stack,"",@progbits

  9. #9
    Invité
    Invité(e)
    Par défaut
    Merci pour ces infos. Honnêtement, je n'ai pas regardé les listings d'assembleur en détail mais si je comprends bien bind et lambda ajoutent un léger surcoût en -O0 par rapport à la fonction directe mais ce surcoût est éliminé en -O3 ?

    Donc, ok; c'est bon à savoir. Maintenant, ne m'en veux pas mais je continue à penser que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    auto f = std::async(std::launch::deferred, resultatBackward, std::cref(monGraphe));
    c'est plus simple que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    auto f = std::async(std::launch::deferred, [&](){return resultatBackward(monGraphe);});
    en plus d'être moins source d'erreur, et que ce n'est pas parce que les lambdas sont à la mode qu'il faut systématiquement les utiliser. Mais c'est une question goût.
    Dernière modification par Invité ; 09/11/2015 à 21h52.

  10. #10
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Je pense que le foncteur et la lambda sont inlinés dès -O1.

    Le première fois que j'ai utilisé std::thread ma variable n'était pas modifiée car je n'avais pas utilisé std::ref que j'ai découvert à ce moment là du coup. Si j'avais utilisé une lambda je n'aurais pas eu de problème.

    La lambda est plus générique, on l'aurait utilisée si on avait voulu exécuter deux fonctions au lieu d'une (dans la même future). Utiliser une lambda dans tous les cas permet d'avoir la même syntaxe.

    Cependant, je pense que la différence entre les deux versions est une histoire de coding style.

  11. #11
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Pour revenir au problème de l’op, il y a confusion entre deux choses :
    - fin du thread (pthread_join attend pour la fin du thread)
    - fin du traitement de recherche du thread (solution disponible)

    On ne peut donc pas utiliser pthread_join pour savoir si une solution est disponible, sauf à recréer un nouveau thread à chaque étape du traitement.

    Il faut donc utiliser une condition variable, qui va servir à « signaler » la fin du traitement. Une seule est nécessaire, on utilise la même pour les deux threads. On va en revanche utiliser deux autres condition_variable (une dans chaque thread), pour savoir quand démarrer un traitement.

    Le programme principal va donc ressembler à ça :

    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
     
    // partagés par tous les threads
    pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t  condition_var   = PTHREAD_COND_INITIALIZER;
    // partagés par thread principal et thread « backward »
    pthread_mutex_t mut_backward = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond_backward = PTHREAD_COND_INITIALIZER;
    bool stop_backward_thread = false;
    // partagés par thread principal et thread « forward »
    pthread_mutex_t mut_forward = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond_forward = PTHREAD_COND_INITIALIZER;
    bool stop_forward_thread = false;
     
    // créer thread 1 et thread 2 de traitement, inactifs pour l’instant (par exemple, au moyen d’un mutex)
    while(!finalSolutionFound)
    {
        pthread_mutex_lock(&mut);
        // lancer traitement sur thread 1
        pthread_mutex_lock(&mut_forward);
        pthread_cond_signal(&cond_forward);
        pthread_mutex_unlock(&mut_forward);
        // lancer traitement sur thread 2
        pthread_mutex_lock(&mut_backward);
        pthread_cond_signal(&cond_backward);
        pthread_mutex_unlock(&mut_backward);
        while(!solution1Found && !solution2Found)
        {
            pthread_cond_wait( &condition_var, &mut);
        }
        // ici, les deux solutions ont été trouvées. On compare donc les deux solutions, et si ce n’est pas bon, on continue
        // la boucle en renseignant de nouvelles valeurs pour le traitement de thread1 et thread2
     
        // traiter les solutions
        solution1Found = false;
        solution2Found = false;
     
        pthread_mutex_unlock(&mut); // ne pas oublier de « délocker » le mutex, il est locké après que la condition_variable ait été notifiée
    }
    pthread_mutex_lock(&mut_forward);
    stop_forward_thread = true;
    pthread_cond_signal(&cond_forward);
    pthread_mutex_unlock(&mut_forward);
    pthread_mutex_lock(&mut_backward);
    stop_backward_thread = true;
    pthread_cond_signal(&cond_backward);
    pthread_mutex_unlock(&mut_backward);
    pthread_join_all(...)
    pthread_join()
    Et le thread de traitement
    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
     
    pthread_mutex_lock(&mut_forward);
    while(! stop_backward_thread) // resp forward)
    {
        pthread_cond_wait(&cond_forward, &mut_forward);
        pthread_mutex_unlock(&mut_forward);
        if(!stop_backward_thread)
        { 
            // trouver solution, la stocker dans variable partagée prévue à cet effet
            pthread_mutex_lock(&mut);
            solution1found = true; // resp solution2found
            pthread_cond_signal(&condition_var);
            pthread_mutex_unlock(&mut);
        }
    }
    // terminer le thread
    Le tout écrit à la rache au kilomètre, mais c’est l’idée (il y a plein de choses à faire, notamment sortir les paramètres dans une structure ad-hoc pour éviter la duplication, etc).

Discussions similaires

  1. Réponses: 2
    Dernier message: 17/10/2013, 23h39
  2. Réponses: 7
    Dernier message: 25/03/2013, 08h10
  3. Réponses: 1
    Dernier message: 23/03/2013, 22h21
  4. Réponses: 6
    Dernier message: 13/09/2010, 16h02
  5. synchronisation entre deux threads
    Par chabfive dans le forum Concurrence et multi-thread
    Réponses: 9
    Dernier message: 03/11/2006, 12h17

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