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 :

Il était une fois 3 threads: A, B et C . . .


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Par défaut Il était une fois 3 threads: A, B et C . . .
    Bonjour à tous,

    J'explique mon petit problème à l'aide d'un exemple simple. Je suis certain que la réponse est aussi très simple.

    Admettons que nous ayons 3 threads dans un processus: soit les threads A, B et C. Chaque thread peut manipuler une liste unique, celle-ci est donc partagée entre ces 3 threads.
    Les threads A et B ne font que lire dans la liste. Par contre le thread C effectue une écriture (ajoute ou retire des éléments).
    Je ne veux pas introduire de synchronisation entre A et B puisque ces threads ne font qu'une lecture. Le thread C, par contre, doit pouvoir écrire dans la liste dès que possible. Pendant cette écriture, ni A, ni B ne peut lire au risque de manipuler une valeur aberrante.

    En développant un peu le problème, je peux dire que le thread C doit donc attendre qu'il n'y ait plus de lecture avant de pouvoir écrire. Dès que le thread C écrit, les threads de lecture A et B ne peuvent plus lire pendant toute la durée de l'écriture.

    J'ai résumé le problème en utilisant 2 threads de lecture. Dans un cas pratique, je peux en avoir bien plus.

    Que faudrait-il utiliser comme méthode?
    Quel objet de synchronisation est le plus adapté (mutex, sémaphore, section critique, etc.)?
    Des suggestions?

    Merci pour vos remarques, vos idées, etc.

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    C'est ce qu'on appelle le modèle lecteurs/écrivain.

    Si tu as la possibilité de réserver d'un coup plusieurs ressources sur un sémaphore (ce qui n'est pas le cas sous Windows, par exemple), alors je pense que le sémaphore est le plus adapté.

    Sinon, il faut au moins un compteur "lecteurs", un événement manuel "lecture possible" (plus d'écrivain, activé par l'écrivain quand il libère), un autre événement manuel "écriture possible" (activé par le dernier lecteur qui libère lorsque que son compteur atteint zéro) et sans doute quelques mutexes/sections critiques pour protéger le compteur.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    285
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Octobre 2007
    Messages : 285
    Par défaut
    Bonjour Médinoc,

    Si l'accès en écriture/lecture à la liste est simple (du type, une fonction de lecture pour A, une pour B, et une fonction écriture pour C), j'opterais pour un mutex.

    Lorsque A (ou B) doit lire, il active le mutex, bloquant l'accès à son (ses) homologue(s) ainsi qu'à C.
    Pareil pour C lors de l'écriture.
    Libération du mutex à la fin de la fonction.
    Boucle d'attente de libération de mutex en début de fonction.

    Mais cette technique empêche les "classes" de lecture d'effectuer des lectures en simultané, mais à mon gout, ce n'est pas plus mal.

    Cdt

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2005
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2005
    Messages : 41
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Si tu as la possibilité de réserver d'un coup plusieurs ressources sur un sémaphore (ce qui n'est pas le cas sous Windows, par exemple), alors je pense que le sémaphore est le plus adapté.
    Je confirme, je suis bien sous Windows.
    Je comprend ta méthode. Les compteurs sont nécessaire mais ils doivent être protégés. En protégeant les compteurs, il ne faut pas que je synchronise mes threads de lecture...
    Citation Envoyé par JeromeBcx
    Si l'accès en écriture/lecture à la liste est simple (du type, une fonction de lecture pour A, une pour B, et une fonction écriture pour C), j'opterais pour un mutex.
    La méthode de lecture est supposée être la même pour les threads de lecture (A et B).
    Par contre, je ne peux pas introduire de délai de synchronisation entre deux lectures. Je dois respecter un délai de fonctionnement. Ma méthode ne peux pas introduire de délai trop long à chaque lecture car celle-ci peut-être utilisée par n'importe qui, dans un thread "client".

    Une chose importante à noter, je ne connais pas, à priori, le nombre de thread de lecture.

  5. #5
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    285
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Octobre 2007
    Messages : 285
    Par défaut
    Citation Envoyé par raphael_kindt Voir le message
    Je confirme, je suis bien sous Windows.
    Je comprend ta méthode. Les compteurs sont nécessaire mais ils doivent être protégés. En protégeant les compteurs, il ne faut pas que je synchronise mes threads de lecture...
    Pour les compteurs, un Interlock exchange (sous MFC si mes souvenirs sont bon ou une fonction similaire) permet de modifier un long en toute sécurité sans pour autant prendre du temps.
    Néamoins, la protection du compteur reste "léger" et ne devrait pas perturber la lecture. Après tout dépend du contexte...

  6. #6
    Expert confirmé

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Billets dans le blog
    3
    Par défaut
    Si c'est du windows pur....

    Statiques:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    long     nReaders = -1;
    HEVENT hReaders= CreateEvent(NULL,TRUE,FALSE,NULL);
    HEVENT hMutex = CreateEvent(NULL,FALSE,TRUE,NULL);
    HEVENT hWriterMutex = CreateMutex(NULL,FALSE,NULL);
    Lecteurs infinis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    if (InterlockedIncrement(&nReaders) == 0) {
        WaitForSingleObject(hMutex, INFINITE);
        SetEvent(hReaders);
    }
    WaitForSingleObject(hReaders,INFINITE);
     
    ... read safely ...
     
    if (InterlockedDecrement(&nReaders) < 0) {
       ResetEvent(hReaders);
       SetEvent(hMutex);
    }

    1 seul écrivain:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    WaitForSingleObject(hWriterMutex,INFINITE);
    WaitForSingleObject(hMutex, INFINITE);
     
    ... do the writing....
     
    SetEvent(hMutex);
    ReleaseMutex(hWriterMutex);
    J'ai corrigé une ou deux coquilles, avant de donner l'explication de texte...
    L'idée c'est le signal "maitre" {hMutex} qui est en mode de reset automatique (dès qu'un 'wait' a été satisfait). Ca signification est: "personne ne fait rien". Seul le tout premier reader (et le writer) s'en occupe... les autres readers se basent sur un autre signal: {hReaders} à état permanent (nécessite un reset explicite) qui signifie "les readers, on peut y aller à fond les manettes".
    Quant aux writers, il utilisent leur propre mutex autour, histoire de pas cadenasser sans interruption les lecteurs...

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Une autre façon de gérer le lecteur/écrivain est avec une structure lock-free. L'idée est que le lecteur travaille en direct sur la donnée, sans lecture, si l'écrivain veut modifier des choses, il fait une copie locale des données (ce qui peut être cher), travaille dessus, puis échange les nouvelles données avec les ancienne lors d'une seule opération atomique (sans lock donc). Lire par exemple http://erdani.org/publications/cuj-2004-10.pdf
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par nicroman Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if (InterlockedIncrement(&nReaders) == 0) {
        WaitForSingleObject(hMutex, INFINITE);
        SetEvent(hReaders);
    }
    WaitForSingleObject(hReaders,INFINITE);
    Pas de "else"?
    Citation Envoyé par nicroman Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    WaitForSingleObject(hWriterMutex,INFINITE);
    WaitForSingleObject(hMutex, INFINITE);
     
    ... do the writing....
     
    SetEvent(hMutex);
    ReleaseMutex(hWriterMutex);
    Quant aux writers, il utilisent leur propre mutex autour, histoire de pas cadenasser sans interruption les lecteurs...
    Je ne comprends pas : à quoi sert hWriterMutex? Qu'est-ce qu'il garanti?

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

Discussions similaires

  1. Que faire une fois le thread exécuté ?
    Par Coussati dans le forum Langage
    Réponses: 10
    Dernier message: 09/12/2014, 19h47
  2. Il était une fois un objet qui voulait devenir array()
    Par mitchreward dans le forum Langage
    Réponses: 3
    Dernier message: 06/10/2012, 09h59
  3. Il était une fois les liens
    Par @po©alypse dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 07/07/2010, 15h40
  4. [Blague]Il était une fois à Java
    Par el_slapper dans le forum La taverne du Club : Humour et divers
    Réponses: 6
    Dernier message: 29/04/2008, 11h20
  5. [Thread]run une fois et plus apres
    Par maxvador dans le forum Concurrence et multi-thread
    Réponses: 4
    Dernier message: 29/12/2004, 15h31

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