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 :

ordre de reprise de threads bloqués


Sujet :

C#

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut ordre de reprise de threads bloqués
    bonjour,

    Je me demandais si, en C#, des threads bloqués se débloquaient dans leur ordre d'arrivée. Ex :

    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
    public void fonctionGlobale ()
    {
       //creation de cinq objets
       Object o1 = new Object();
       Object o2 = new Object();
       Object o3 = new Object();
       Object o4 = new Object();
       Object o5 = new Object();
     
       //création et lancement des threads
       Thread t1= new Thread(new ParameterizedThreadStart(functionLock));
       Thread t2= new Thread(new ParameterizedThreadStart(functionLock));
       Thread t3= new Thread(new ParameterizedThreadStart(functionLock));
       Thread t4= new Thread(new ParameterizedThreadStart(functionLock));
       Thread t5= new Thread(new ParameterizedThreadStart(functionLock));
       t1.Start(o1); 
       t2.Start(o2); 
       t3.Start(o3); 
       t4.Start(o4); 
       t5.Start(o5); 
      //ici on a 5 threads qui tournent ensemble, lekelle se réveillera le premier?
    }
     
    public static void functionLock(Object o){
          lock (locker) //partie critique
          {
              Sleep(5000);
              Console.Writeline(o.ToString());
           }
     
    }
    Dans le code suivant, est ce que l'ordre d'affichage des objets sera toujours o1, o2, o3, o4 et o5 ? Après kk tests ca à l'air d'être ca mais je n'ai pas trouvé de doc dessus pour etre sûr...
    merci

  2. #2
    Membre éclairé
    Avatar de Emerica
    Profil pro
    Consultant
    Inscrit en
    Juillet 2003
    Messages
    190
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2003
    Messages : 190
    Points : 724
    Points
    724
    Par défaut
    Il n'y a en effet aucune certitude que tes threads repartent systématiquement dans l'ordre de départ. Le seul moyen est d'imposer leur synchronisation.
    Some of the world's greatest feats were accomplished by people not smart enough to know they were impossible.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Imposer leur synchronisation? cad?

  4. #4
    Membre éclairé
    Avatar de Emerica
    Profil pro
    Consultant
    Inscrit en
    Juillet 2003
    Messages
    190
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2003
    Messages : 190
    Points : 724
    Points
    724
    Par défaut
    Ca dépend beaucoup de ce que tes threads font. Une manière courante est d'utiliser des évenements pour les synchroniser (ie AutoResetEvent et ManualResetEvent). La base se trouve ici.

    Utiliser Sleep rend le thread éligible pour exécution par le scheduler mais ne garantit pas que l'ordre d'exécution des threads soit similaire à leur ordre d'élection. Dans tous les systèmes multithreading, l'ordre n'est pas prévisible.
    Some of the world's greatest feats were accomplished by people not smart enough to know they were impossible.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    En faite je voudrais juste que mes threads soit lancés dans leur ordre d'arrivée. Est ce que je dois implémenter moi meme une sorte de liste FIFO pour géré cela ou il y a déja quelque chose qui existe (d'ailleur peut on choisir le thread à réveiller à partir d'un id?) ?

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Tu veux faire quoi exactement ? Par ce que moi j'ai bien une solution pour toi : ne pas utiliser de thread supplémentaire, ce qui donne :

    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
     
    public void fonctionGlobale ()
    {
       //creation de cinq objets
       Object o1 = new Object();
       Object o2 = new Object();
       Object o3 = new Object();
       Object o4 = new Object();
       Object o5 = new Object();
     
       //création et lancement des non-threads
       functionLock(o1);
       functionLock(o2);
       functionLock(o3);
       functionLock(o4);
       functionLock(o5);
    }

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Je veux pouvoir lancer functionLock(o2); functionLock(o3); etc sans avoir à attendre le retour de la fonction functionLock(o1) et je veux que l'exécution des sections critiques se fasse d'abord sur l'objetc o1 puis o2, puis o3

  8. #8
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Dans ce cas tu peux utiliser les events comme l'a dit Emerica. En gros ce sont des objets qui ont deux états, signalé ou pas signalé. Un thread peut attendre qu'un évenement soit signalé ou bien lui même signaler un évènement.

    Tu donnes deux Event à chacune de tes fonctions en plus de ses paramètres, un sur lequel elle vont attendre et un autre qu'elles vont signaler. L'event qu'une fonction signale est l'event sur lequel la fonction suivante va attendre. De cette manière chaque fonction ne pourra entrer dans la section critique que lorsque la précédente a signalé son event. Tu vois l'idée ?

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    ok, merci je vois un peu l'idée. Par contre après lecture du lien de Emerica et de quelques pages sur les events, je n'arrive pas à saisir la différence entre un AutoResetEvent et un ManualResetEvent ?

  10. #10
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Un AutoresetEvent ne va signaler qu'un seul thread puis reprendre son état non signalé. Si plusieurs threads attendent que le même AutoresetEvent soit signalé, un seul sera libéré au moment du signalement puis l'event sera de nouveau non signalé.

    Un ManualResetEvent va laisser passer tous les threads qui attendent qd il est signalé et il va garder son état signalé jusqu'a ce qu'un thread appelle la méthode reset dessus.

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Suite aux réponses je suis arrivé au code suivant :

    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
    //Fonction à lancer
    public void fonctionGlobale()
    {
            int nbThread = 50;
     
            //creation des 
            AutoResetEvent eventDattente = new AutoResetEvent(false);
            AutoResetEvent eventADeclenche = new AutoResetEvent(false);
            eventDattente.Set();//le premier thread peu partir directement
            for (int j = 0; j < nbThread; j++)
            {
                    Thread t = new Thread(new ParameterizedThreadStart(functionCritik));
            ObjectXEventXEvent anObj = new ObjectXEventXEvent();
            anObj.i = j;
                    anObj.eventASignaler = eventADeclenche;//il signalera sur le new2 donc l'autre attend sur le new2
            anObj.eventAAttendre = eventDattente;
            t.Start(anObj); //lancement du thread
            eventDattente = eventADeclenche; //le prochain thread attendra sur l'event que le dernier thread lancé déclenchera
            eventADeclenche = new AutoResetEvent(false);//prochain thread declenché
            }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //fonction contenant la partie critique
    public void functionCritik(Object o)
    {
            ObjectXEventXEvent obj = (ObjectXEventXEvent)o;
            obj.eventAAttendre.WaitOne();//début de la section critique
            Thread.Sleep(1000);
            Console.WriteLine(obj.i);
            obj.eventASignaler.Set();//fin de la section critique
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //classe créée car ParameterizedThreadStart prend en paramètre une fonction du type
    //void maFonction (Object)
    //ainsi il faut encapsuler dans un seul objet les 3 objets (2 event et l'entier)
    public class ObjectXEventXEvent
    {
            public AutoResetEvent eventASignaler;
            public AutoResetEvent eventAAttendre;
            public int i;
    }
    Normalement cela devrait fonctionner comme attendu (affichage dans l'ordre croissant des nombres), après plusieurs test je n'ai pas eu d'exemple me prouvant le contraire.

    Par contre j'ai de nouveau un problème si je lance dans 2 threads différents les fonctions globales :
    -pour une fonction globale donnée, on a l'affichage dans l'ordre de lancement des threads :
    eg :
    1 (de la fonction globale 1)
    1 (de la fonction globale 2)
    2 (de la fonction globale 1)
    3 (de la fonction globale 1)
    2 (de la fonction globale 2)
    4 (de la fonction globale 1)
    5 (de la fonction globale 1)

    etc..

    Par contre je n'ai pas une prioritée fifo sur l'appel de la functionCritik entre les 2 fonctions.
    EG :
    dans le temps :
    la fonction globale 1 lance le thread numéro 1
    la fonction globale 2 lance le thread numéro 1
    la fonction globale 1 lance le thread numéro 2
    la fonction globale 2 lance le thread numéro 2
    la fonction globale 3 lance le thread numéro 1
    la fonction globale 3 lance le thread numéro 2

    A l'affichage je peux avoir :
    1 (de la fonction globale 1) //ok
    2 (de la fonction globale 1) //ok
    1 (de la fonction globale 2) //ok
    2 (de la fonction globale 2) //ok
    3 (de la fonction globale 2) //pas ok

    Quelqu'un a une idée pour empécher ca?

  12. #12
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    J'ai décidemment du mal à comprendre ce que tu veux obtenir exactement et pourquoi.

  13. #13
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Bon je vais essayer de faire moi généric que mes prints. En faite je veux faire une sorte de mutiplexeur avec des appels asyncrhones dessus :

    -j'ai donc un objet mutiplexeur (M). Il a une fonction publique add(message) qui prend en parametre un message et le traite.

    -j'ai plusieurs threads qui tournent et ils génèrent des messages (tous les threads génèrent les même types de message) et appellent M.add pour chacun de ce message.

    -Un seul message peut etre traité à la foi par M.

    Je veux que le traitement des messages se fasse dans l'ordre d'appel de la fonction M.add.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public class multiplexeur
    {
       public void add(message m)
       {//partie critik   }
    }
    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
    public class sender
    {
       public delegate void monDelegate(message m);//meme signature que Multiplexeur.add
       private monDelegate traiterUnMessage;
       public void run()
       {
           while (true)
              traiterUnMessage(new message());
       }
     
       public void settraiterUnMessage(monDelegate uneFonctinon)
       {
           traiterUnMessage = uneFonctinon;
       }
    }
    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
    //fonction principale
       main()
       {
          multiplexeur M = new Multiplexeur();
          sender s1 = new Sender();
          sender s2 = new Sender();
          ...
     
          s1.settraiterUnMessage(M.add);
          s2.settraiterUnMessage(M.add);
          ...
     
          Thread t1 = new Thread(new ParameterizedThreadStart(s1.run));
          Thread t2 = new Thread(new ParameterizedThreadStart(s2.run));
          ...
     
          t1.start();
          t2.start();
          ...
        }
    Donc t1, t2, ...tn appellent M.add(unMessage), si la partie critik est bloquée alors les threads suivants sont mis en attente. Une fois sortie de la partie critik, un nouveau thread peu rentrer dedans. Je veux que ce nouveau thread soit le premier a s'être bloqué dessus.

    Si je ne suis toujours pas clair, n'hesite pas a poser des questions..

  14. #14
    Membre éclairé
    Avatar de Emerica
    Profil pro
    Consultant
    Inscrit en
    Juillet 2003
    Messages
    190
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2003
    Messages : 190
    Points : 724
    Points
    724
    Par défaut
    Pourquoi ne pas utiliser plus simplement le mot clé lock?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    private void TraiteMessage(Message m)
    {
       lock(this)
       {
          // ...
       }
    }
    Un seul thread ne sera admis à l'intérieur du bloc de code protégé. Les suivants seront automatiquement mis dans une queue.
    Some of the world's greatest feats were accomplished by people not smart enough to know they were impossible.

  15. #15
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Aaahhh ok c'est tout. Dans ce cas je changerais simplement un peu ton architecture, car tout ce dont tu as besoin c'est d'une file d'attente.

    Je vois :
    - une variable membre privée dans ta classe multiplexeur
    de type Stack
    - ta méthode addMessage se contente d'ajouter le message à la liste de manière synchronisée
    - le constructeur de ta classe multiplexeur crée un thread qui se contente d'attendre qu'il y ait un message dans la stack et le traite puis continue de boucler.

    Voilà pour le principe. Quand ta classe multiplexeur a fini de traiter un message elle pourrait aussi le signaler au thread appelant si nécessaire (par l'utilisation d'Event). Tu pourrais même t'amuser à implémenter le pattern asynchone de .net , c'est à dire proposer une méthide Add synchrone (qui bloque le thread appelant jusqu'à ce que le message soit traité) et des méthodes BeginAdd et EndAdd pour le traitement asynchrone.

    EDIT: C'est la classe Queue qu'il faut utiliser pour une pile FIFO si on en croit msdn. Stack est LIFO.

  16. #16
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Citation Envoyé par Emerica
    Pourquoi ne pas utiliser plus simplement le mot clé lock?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    private void TraiteMessage(Message m)
    {
       lock(this)
       {
          // ...
       }
    }
    Un seul thread ne sera admis à l'intérieur du bloc de code protégé. Les suivants seront automatiquement mis dans une queue.
    Tu es sûr ? Il me semble que rien ne garanti l'ordre dans lequel les threads qui attendent un lock vont être éveillés. Tu l'as dit toi même plus haut:

    Dans tous les systèmes multithreading, l'ordre n'est pas prévisible.

  17. #17
    Membre éclairé
    Avatar de Emerica
    Profil pro
    Consultant
    Inscrit en
    Juillet 2003
    Messages
    190
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2003
    Messages : 190
    Points : 724
    Points
    724
    Par défaut
    Autant pour moi, j'avais oublié l'ordre. La "queue" du mot-clé lock est FIFO mais rien ne garantit que d'autres facteurs choisissent à la place du runtime.
    Some of the world's greatest feats were accomplished by people not smart enough to know they were impossible.

  18. #18
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Oui l'idée de la queue me plait bien, je vais voir ce que cela donne. Merci.

  19. #19
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 69
    Points : 30
    Points
    30
    Par défaut
    Finalement avec une queue cela marche comme je veux voici en gros ce que cela donne :
    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
     
        public class Multiplexeur
        {
            private Queue entryQueue = new Queue();//queue d'entrée des messages
     
            public Multiplexeur()
            {
                Thread aThread = new Thread(new ThreadStart(run));
                aThread.Start();
            }
     
            private void run()
            {
                while(true)
                {
                    if (entryQueue.Count != 0)
                        traiterUnMessage(entryQueue.Dequeue());
                }
            }
     
            //point d'entrée du mutliplexeur
            public void add(Object mess)
            {
                entryQueue.Enqueue(mess);
            }
     
        }
    Je me posais quand meme la question suivante : dois-je mettre un lock lorsque j'utilise le champ "entryQueue". Car est ce que les fonctions Dequeue et Enqueue sont indivisible ou est ce que le schema suivant peut se passer :
    -un thread t1 appelle M.add et commence le Enqueue
    -le scheduleur passe la main a un thread t2 avant que le Enqueue soit fini
    -t2 appelle M.add et donc le Enqueue

    Est ce que cela ne va pas poser un probleme?

  20. #20
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2003
    Messages
    835
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2003
    Messages : 835
    Points : 1 046
    Points
    1 046
    Par défaut
    Désolé pour ma réponse tardive, j'étais en vacances .

    Je pense que par défaut les méthodes enqueue/dequeue ne sont pas thread safe (regarde la doc sur ces objets pour en être sûr) et alors il faut effectivement synchroniser l'accés à ta file.

Discussions similaires

  1. ordre d'execution de thread
    Par Etudiante_Ines dans le forum Général Java
    Réponses: 5
    Dernier message: 14/03/2012, 09h03
  2. [Visual C#] Reprise de threads bloqués
    Par levalp dans le forum Windows Forms
    Réponses: 4
    Dernier message: 22/02/2008, 22h25
  3. L'ordre d'exécution des threads
    Par Dosseh dans le forum Modules
    Réponses: 1
    Dernier message: 05/07/2007, 20h24
  4. Réponses: 2
    Dernier message: 05/03/2007, 16h45
  5. pause et reprise de thread
    Par raggadoll dans le forum C++Builder
    Réponses: 5
    Dernier message: 12/07/2003, 10h50

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