Partage
  • Partager sur Facebook
  • Partager sur Twitter

Thread - Lancé un thread quand un autre s'arrête

Gestion de fille d'attente

    18 octobre 2018 à 18:01:28

    Bonjour, je suis entrain de travailler sur les threads pour un TP et j'ai beaucoup de difficulté avec ces derniers.

    On nous a demandé de gérer une fille d'attente de 50 visiteurs pour un zoo pouvant en contenir que 10. J'ai essayé plusieurs "solutions" mais je n'arrive vraiment pas à comprendre comment fonctionne les threads. 

    La "solution" qui semble le plus convenir semble être de créer un compteur et au delà de 10 interrompre les autres threads (les 40 autres) , mon soucie est que je n'arrive pas à faire en sorte que lorsqu'un Thread (visiteur) a fini sa visite un autre visiteur en attente puisse entrée dans le zoo. 

    Voici quelques méthode, je ne sais pas si ma démarche est bonne. Si vous avez des conseils ou des pistes ça serait avec grand plaisir.

    public class Visiteur implements Proie, Runnable {
    private Thread t;
    public static int compteur;
    ...
    
    public void run() {
    		Visiteur.compteur++;
    		
    		if (Visiteur.compteur > 10) {
    			try {
    				this.t.join();
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		
    		this.accederZoo();
    		
    		System.out.println(this.t.getName() + " " + this.dansLeZoo);
    
    		this.sortirDuZoo();
    }
    
    public void accederZoo() {
    		this.dansLeZoo = true;
    
    	}
    
    public void sortirDuZoo() {
    		this.dansLeZoo = false;
    		this.notify();
    	}
    }



    • Partager sur Facebook
    • Partager sur Twitter
      18 octobre 2018 à 19:21:11

      L'idée c'est d'utiliser les zone critique, c'est à dire une portion de code qui n'est accessible que par un thread à la fois.

      Dans une zone critique tu regardes si le thread peut rentrer dans le zoo, si il peut alors tu incrémentes un compteur, si il ne peut pas à alors il attend. De même pour la sortie sauf que ce coup si le compteur se décrémente. 

      Il faut faire attention lors de l'utilisation de variables partagées en lecture, il faut s'assurer que le thread accède bien à la dernière valeur de la variable. Pour ça il existe plusieurs manière ( verrou, sémaphore, variables atomiques ... )

      Je vois plusieurs problème dans ton code : tu ne peux pas assurer qu'il y ai maximum 10 visiteur dans ton zoo et tu ne dois pas faire ".join" si il ne peut pas rentrer, soit tu fais de l'attente active (le visiteur essaye de rentrer jusqu'à ce qu'il puisse) soit tu "l'endors" et le "reveil" lorsqu'un visiteur sort

      -
      Edité par Splintz 18 octobre 2018 à 19:26:00

      • Partager sur Facebook
      • Partager sur Twitter
        18 octobre 2018 à 19:24:04

        Salut,

        Je ne sais pas ce que tu as le droit d'utiliser.
        En vrai pour ce genre de truc on utiliserait soit sans doute un framework avec une file JMS ou alors un ExecutorService avec un pool de 10 thread  https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

        En tout cas on utiliserait plutôt un des classes https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html plutôt que directement de join ou notify.

        Mais je suppose que le but de ton TP c'est justement de comprendre un peu comment ça fonctionne et donc d'utiliser plutôt des synchronized / wait / notify.
        Du coup je serais surpris que tu n'ai pas eu un cours pour t'expliquer ces notions.
        Si c'est pas le cas, cherche "java synchronized wait notify" sur google tu devrais trouver des trucs.

        • Partager sur Facebook
        • Partager sur Twitter
          18 octobre 2018 à 19:43:41

          Merci pour vos réponses, en guise de court nous avons un exemple mais il est difficilement applicable sur ce TP et oui le but et de nous faire travailler sur les notify(), join() etc...

          Pour ce qui est des zones critiques, la piste m'éclaire un peu, merci ;) Question comment faire pour que une portion de code ne soit accessible que par un thread à la fois ?

          • Partager sur Facebook
          • Partager sur Twitter
            18 octobre 2018 à 20:23:30

            Splintz a écrit:

             Pour ça il existe plusieurs manière ( verrou, sémaphore, variables atomiques ... )

            commence par regarder ca

            • Partager sur Facebook
            • Partager sur Twitter
              19 octobre 2018 à 9:50:27

              La maitrise des mécanismes de Threads, sections critiques & co est un des point qui pose le plus souvent problème aux développeurs, car il s'agit d'ajouter une dimension DYNAMIQUE à la programmation, là ou beaucoup de gens n'arrivent pas à dépasser une vision STATIQUE.

              Les conseils qui t'ont été donnés ci-dessus vont dans le bon sens : ton TP repose sur l'utilisation de trois outils, pas plus : wait, notify, synchronized. En plus de la classe Thread et de l'interface Runnable bien entendu.

              Un autre conseil : essaye de modéliser ton problème de façon aussi naturelle que possible, et pas d'inventer "un truc technique". Je m'explique...

              Tu nous fournis un bout de code, une class Visiteur, qui tente de résoudre a elle seule le problème.

              Conceptuellement, ton projet comporte 3 notions : le visiteur, le zoo, la file d'attente (ce que j'appelerai le guichet, car c'est lui qui régule)

              Si tu utilises ces trois notions, ta compréhension du problème peut être plus facile : le but pour un visiteur est d'OBTENIR l'aurisation, de la part du GUICHET, d'ACCEDER à la ressource critique ZOO, puis une fois qu'il a obtenu cette autorisation, il CONSOMME la ressource et, enfin, il SIGNALE qu'il a terminé de consommer au guichet de sorte que celui-ci puisse à nouveau AUTORISER un autre visiteur...ceux-ci étant en ATTENTE dans une file tant que cette condition n'est pas possible.

              Dans cette phrase, les verbes et les entités nommées correspondent à des réalités de ton programme.

              Tiens nous au courant de la suite...

              • Partager sur Facebook
              • Partager sur Twitter
                19 octobre 2018 à 18:57:11

                L'un des premiers truc à faire attention, c'est de "synchroniser" l'incrémentation et la décrémentation de la variable "compteur" ou "placeRestantes" pour empécher que 2 threads ne la change pas en même temps.

                synchronized(Visiteur.compteur)  //j'empêche les autres Thread de rentrer dans un code qui utilise le verrou "Visiteur.compteur"
                {
                    Visiteur.compteur++;
                }

                ou

                private synchronized void increment()
                {
                    Visiteur.compteur++;
                }


                Aussi, je pense que tu devrais faire cette synchronisation dans les fonctions accederZoo() et sortirDuZoo()

                public void accederZoo() {
                    synchronized(Visiteur.compteur)
                    {
                        Visiteur.compteur++;
                        if(Visiteur.compteur >= 10
                        {
                            //j'attend
                        }
                    }
                    this.dansLeZoo = true;
                }
                
                public void sortirDuZoo()
                {
                    this.dansLeZoo = false;
                    synchronized(Visiteur.compteur)
                    {
                        Visiteur.compteur--;
                        //dire que quelqu'un peu entrer  //ici et pas après le synchronized parce qu'un autre visiteur fraichement créé pourrait passer devant tout le mode vu que compteur < 10
                    }
                }

                Autre problème, ça devrait être une classe "Zoo" qui, s'il passe de l'état "plein" à l'état "non(plein)" devrait notifier les visiteurs.

                • Partager sur Facebook
                • Partager sur Twitter
                  21 octobre 2018 à 18:18:10

                  Merci pour ces réponses exhaustives. J'avoue avoir encore du mal.

                  En gros j'essaie de syncroniser mes méthodes accederZoo() et sortirZoo().

                  Dans la première je lance un wait() une fois que le compteur est à 10 pour stopper les autres Threads, j'exécute le traitement dans mon run() (juste attendre 5 secondes) et une fois fini je lance sortirZoo() qui contient un notify() pour reprendre le cour des Thread.  

                  En gros mon programme fait entré les 10 visiteurs. Ensuite il lance des exceptions (pourtant mes méthodes ont l'air synchronisé) et ensuite il me sort les 10 visiteurs qui était entrés sans pour autant en faire entré un autre. 

                  public class Visiteur implements Proie, Runnable {
                  	public static int compteur;
                  	private boolean dansLeZoo;
                  	private Thread t;
                  
                  	@Override
                  	public void run() {
                  		this.accederZoo();
                  
                  		if (this.dansLeZoo) {
                  			System.out.println(this.t.getName() + " " + "est entrée dans le zoo");
                  
                  			try {
                  				Thread.currentThread().sleep(5000);
                  			} catch (InterruptedException e) {
                  
                  			}
                  
                  			this.sortirDuZoo();
                  		}
                  	}
                  
                  
                  	public synchronized void accederZoo() {
                  
                  		Visiteur.compteur++;
                  
                  		if (Visiteur.compteur >= 10) {
                  			try {
                  				this.t.wait();
                  			} catch (InterruptedException e) {
                  				// TODO Auto-generated catch block
                  				e.printStackTrace();
                  			}
                  		}
                  
                  		this.dansLeZoo = true;
                  	}
                  
                  	public synchronized void sortirDuZoo() {
                  		this.dansLeZoo = false;
                  
                  		Visiteur.compteur--;
                  		System.out.println(this.t.getName() + " " + "est sortie du zoo");
                                  this.t.notify()
                  	}
                  
                  }
                  

                  Je m'entête peut-être dans la mauvaise direction car je ne vois pas de solution viable avec ma méthode de conception.

                  -
                  Edité par Meh 21 octobre 2018 à 18:18:27

                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 octobre 2018 à 12:58:57

                    Je reviens sur ce que j'ai dit, mais tu devrais séparer tes objets, tu comprendrais mieux d'ou vient ton erreur.

                    Actuellement, tu as mis la variable compteur en static, ce qui en fait une variable GLOBALE à toutes tes instances. Tu as utilisé cet artifice pour avoir un compteur partagé, placé au sein de ta classe Visiteur.

                    Par contre, tes traitements accederAuZoo et sortirDuZoo sont des traitements liés aux instances (pas des fonctions statiques).

                    Du coup, quand tu appliques synchronized sur ces deux méthodes, le monitor d'objet visé est celui (implicite) de l'instance de Visiteur qui effectue l'appel.

                    Tu as 50 instances de Visiteur en mémoire, donc 50 moniteurs de synchronization (hérités de la classe Object) différents, et aucun point commun entre eux. Donc tu PENSES avoir fait de la synchro, dans la pratique tu as 50 threads qui exécutent en parallèle un code identique, chacun avec ses objets mémoire, et qui partagent une variable entière static qui ne dispose d'aucune protection.

                    Ce que tu dois comprendre, c'est que WAIT, NOTIFY, SYNCHRONIZED s'appliquent à UN OBJET DONNE, ils manipulent le moniteur (un signal de synchro + une file d'attente) de synchro que tout objet JAVA possède de base, via l'héritage de OBJECT.

                    Pour que tes threads ne se marchent pas sur les pieds, ils doivent appeler les méthodes WAIT / NOTIFY et SYNCHRONIZED sur le même objet, un objet commun (un singleton, dans le jargon) qui coordonne leur travail parallèle.

                    Dans ton code, tu pourrais t'approcher du résultat ainsi. Je te donne le code, mais il faut que tu comprennes la différence fondamentale entre ce que tu as écris et ce que je te donne :

                    public class Visiteur implements Proie, Runnable {
                        private static Integer COMPTEUR_GLOBAL = new Integer(0);
                    
                        private Thread t;
                     
                        @Override
                        public void run() {
                            accederZoo();
                     
                            System.out.println(this.t.getName() + " " + "est entrée dans le zoo");
                     
                                try {
                                    Thread.currentThread().sleep(5000);
                                } catch (InterruptedException e) {
                     
                                }
                     
                            sortirDuZoo();
                        }
                     
                     
                        public void accederZoo() {
                     
                            synchronized(COMPTEUR_GLOBAL) {
                                 while (COMPTEUR_GLOBAL > 9) {
                                      COMPTEUR_GLOBAL.wait();
                                 }
                            
                                 COMPTEUR_GLOBAL++;
                            }
                            System.out.println(this.t.getName() + " " + "est entré dans le zoo");
                        }
                     
                        public void sortirDuZoo() {
                            synchronized(COMPTEUR_GLOBAL) {
                                 COMPTEUR_GLOBAL--;
                                 COMPTEUR_GLOBAL.notify();        
                            }
                            System.out.println(this.t.getName() + " " + "est sortie du zoo");



                    • Partager sur Facebook
                    • Partager sur Twitter

                    Thread - Lancé un thread quand un autre s'arrête

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown