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 :

multithread, probleme de deadlock


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut multithread, probleme de deadlock
    Bonjour.

    lock, semantic, mutex, je ne saurais dire le nombre fois où j'ai lu des articles traitant ces sujets... resultat : 0 je comprend la théorie, mais bute systematiquement en pratique !

    Je tente depuis plusieurs semaines maintenant de corriger un probleme de deadlock (thread qui se bloque de facon totalement aléatoire).

    EDIT : je m'en suis sorti, je donne ici quelques régles que j'ai suivi et qui m'ont permis de résoudre mes problemes

    concretement :Je soupsonne vivement une de mes classe (une FSM).
    disons qu'elle a 2 methodes Read et Write
    Pleins de theads peuvent appeller Read en meme temps
    Pleins de theads peuvent appeller Write en meme temps

    Write déclenche un evenement, et certains threads qui "y sont abonnés"
    sont suspeptible de créer des threads qui appelleront Read
    Bref, c'est l'enfer

    Je pense (malgres mes lacunes) que l'ideal serait de rendre Read prioritaire sur Write.

    Mais je tourne en rond depuis si longtemps maintenant... que faire à part me suicider svp?

    J'aimerai trouver un bon patern qui représente le cas où plusieurs block de code lise et ecrivent sur une meme variable.

  2. #2
    Membre Expert Avatar de meziantou
    Homme Profil pro
    autre
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

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

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Par défaut
    Je pense (malgres mes lacunes) que l'ideal serait de rendre Read prioritaire sur Write.
    Tu devrais peut-être regarder la classe ReadWriteLocker
    http://msdn.microsoft.com/en-us/libr...rlockslim.aspx

    Si tu as un peu de code, ce serait plus facile pour t'aider.

  3. #3
    Membre Expert
    Avatar de GuruuMeditation
    Homme Profil pro
    .Net Architect
    Inscrit en
    Octobre 2010
    Messages
    1 705
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : .Net Architect
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2010
    Messages : 1 705
    Par défaut
    Oui je conseille aussi le ReaderWriterLockSlim.

    Les acces lectures seront concurents, et ecriture exclusifs.

    Un pseudo-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
     
    var lock = new ReaderWriterLockSlim();
    int somedata = 0;
     
    void Lecture()
    {
    lock.EnterReadLock();
    // acces partagé
    lock.EndReaderLock();
    }
     
    void Ecriture()
    {
    lock.EnterWriteLock();
    // acces exclusif
    lock.EndWriteLock();
    }

  4. #4
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut
    Bonjour et merci pour vos réponses.

    Je suis en train d'etudier ReaderWriterLockSlim.

    Mon salut se trouve peut etre dans ses methodes EnterUpgradeableReadLock.
    Car je rencontre tres souvant le probleme suivant:

    1)Entre le moment où la valeur est modifiée (write) et le moment où les autres lisent la nouvelle valeur (read), je dois empecher tout autre thread de faire un write
    2)Lorsqu'un thread veut lire la valeur (read), si elle est en train d'etre modifiée, le thread doit attendre la fin du write.

    Ces 2 contraintes couplées imposent le deadlock Et c'est là en fait où je bloque mentalement

    Voici un petit extrait de code (je suis conscient qu'il ne va pas du tout!):
    (les variables m_XXX etant des champs privés de mon objet)
    N.B : il manque des try/finally pour protéger les locks, mais peut on mettre un return dans un lock?

    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
    class MaFSM
    {
        readonly object sync = new object();
     
       public XmlState Next(string inputArg)
            {
                lock (sync)
                {
                    m_action = String.Empty;
                    m_lastTransition = inputArg;
                    //cherche state == current
                    foreach (XMLStateReact reac in CurrentState.m_reacts)
                    {
     
                        if (reac.Input == inputArg)
                        {
                            m_action = reac.Action;
                            XmlState futurState = FindState(reac.Next);
                            if (futurState == null)
                                throw new ApplicationException(String.Format("ERREUR FSM : le status '{0}' n'a pas pu etre trouvé.\r\nStatus en cours : {1}\r\nDeclencheur : {2}", reac.Next, CurrentState.m_statename, inputArg));
                            CurrentState = futurState; //Le set de CurrentState déclenche l'evenement StateChanged
                            break;
                        }
                    }
     
                    return this.CurrentState;
                }
    }
     
            public XmlState CurrentState
            {
                get
                {
                    lock (sync) return m_stateCurrent;
                }
                private set
                {
                    lock (sync)
                    {
                        m_stateOld = m_stateCurrent;
                        m_stateCurrent = value;
                    }
                    if (StateChanged != null)
                        StateChanged(this, EventArgs.Empty);
                }
            }
    }

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut
    Ca y est, j'ai fais quelques modifs qui semblent porter leur fruits.

    Plutot que de balancer du gros code indigeste, voici les regles que je me suis fixé:
    • Dans la methode writer, j'essaye de rassembler en début de methode tout ce qui peut lire un champ de la classe, quite à recopier ces valeurs dans des variables locales.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      void Write(obj data)
      {
           m_locker.EnterUpgradeableReadLock(m_lockerTimeout);
           String localvar = this.m_toto;
           //...
      }
    • Je rassemble ensuite tout ce qui peut ecrire dans la classe. Je garde la main sur le lock vu que j'avais utilisé EnterUpgradeableReadLock.
    • toujours dans le block qui ecrit, je recopie dans des variables locales, toutes les modification que j'ai fait, dans le but de préparer mon event.
      (XmlStateTransition dérive de EventArg)
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
       
      m_locker.EnterWriteLock();
                          m_action =localvar;
                          m_lastTransition = data;
                          transitInfos = new XmlStateTransition(inputArg, data);
                          m_locker.ExitWriteLock();
    • Une fois sorti de mes locks, j'envoi mon event. L'inconvénient, est que, entre le moment où je lache le lock, et le moment où j'envoi l'event, un autre thread peut etre rentré dans la méthode. Mais il n'y a pas de solution parfaite, je ne sais pas qui est abonné à mon event.
    • j'utilise try/finally. Tous mes Exit sont dans les finally pour m'assurer que le lock soit libéré quoi qu'il arrive. Voici ce que ca donne au final :

      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
       
      void Write(obj data)
      {
           XmlStateTransition transitInfos = null;
           m_locker.EnterUpgradeableReadLock();
           try
           {
                 String localvar = this.m_toto;
                //... autre lectures
       
                m_locker.EnterWriteLock();
                try
                {
                          m_action =localvar;
                          m_lastTransition = data;
                          transitInfos = new XmlStateTransition(inputArg, data);
       
                }
                finally
                {
                    m_locker.ExitWriteLock();
                }
           }
           finally
           {
                 m_locker.ExitUpgradeableReadLock();
           }
           if(transitInfos != null && StateChanged != null)
               StateChanged(this,transitInfos);
      }
    • Je n'utilise plus du tout le mot clé lock, maintenant j'utilise Monitor.Enter, et place Monitor.Exit dans des finally systématiquement.


    PS : j'ai lu dans un livre que System.Threading.ReaderWriterLockSlim etait l'une des technique de synchro les plus lente (et disponible qu'à partir du framework 3.5). Mais ils parlent de quelques micro-secondes de perte.

  6. #6
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Bonjour à toi.

    Voici déjà une amélioration simple : tu n'as pas besoin de verrou sur le getter de CurrentState ! En effet, le seul effet de ce verrou est que, si un thread est en train d'appeler Next, on attend qu'il ait fini. Mais dans 99.99% des cas, on peut très bien se contenter de l'état actuel quand bien même celui-ci est appelé à être modifié sous peu. En somme, ton verrou sur le getter est l'équivalent du fameux : "malheureux, n'achète pas de PC maintenant, dans six mois ils seront plus puissants".

    Ce qui laisse uniquement une contention au niveau de Next lui-même. Ici, trois stratégies possibles :
    * Concurrence pessimiste : c'est ton premier code ou ton second code. Je suis assez dubitatif sur le gain de performances du second à partir du moment où tu auras viré le verrouillage du getter. De même, puisqu'entre EnterUpgradable... et EnterWrite tu n'as qu'une lecture de membre, mieux vaut vraisemblablement acquérir d'emblée un verrou en écriture.
    * Concurrence optimiste : en-dehors de tout verrou on va lire les membres m_currentState, m_action et m_lastTransition et choisir le futurState. Puis, on entre dans un verrou et, si les membres sont toujours égaux aux valeurs que nous avions utilisées, on assigne futurState à CurrentState. Sinon, on repart au début.
    * Revoir le système dans son entier.

    AU passage, note que l'usage du mot-clé lock insère implicitement un bloc try/finally, rien à faire de ce côté donc.

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

Discussions similaires

  1. Proxy thread queue multithread probleme performance
    Par G4uthier dans le forum Programmation et administration système
    Réponses: 2
    Dernier message: 16/08/2011, 11h12
  2. problem com interop multithread
    Par giova_fr dans le forum C#
    Réponses: 1
    Dernier message: 13/08/2008, 16h25
  3. Probleme Client / Server multithread
    Par hakuryu dans le forum C#
    Réponses: 1
    Dernier message: 15/02/2008, 14h11
  4. probleme stack overflow en multithread
    Par ReaderDigest dans le forum C++
    Réponses: 3
    Dernier message: 03/01/2008, 18h12
  5. Réponses: 14
    Dernier message: 31/08/2005, 18h21

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