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 :

rand() et pthread


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Février 2008
    Messages
    67
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 67
    Par défaut rand() et pthread
    Hello,

    Soit le main() 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
    int main(int argc, char* argv[])
    {
     pthread_t ta;
     pthread_t tb;
     pthread_create (&ta, NULL, task_a, NULL);
     pthread_create (&tb, NULL, task_b, NULL);
     srand (time(NULL));
     generenb(NOMBRE);
     #if 1
     pthread_join (ta, NULL);
     pthread_join (tb, NULL);
     #endif
     
     system("PAUSE");    
     return 0;
    }
    Dans la fonction que je duplique avec les thread, je génère une suite de nombre que je veux à chaque fois différente d'un thread à l'autre.

    Lorsque je n'ai qu'un seul thread (le a), la fonction generenb() ainsi que mon thread fonctionnent bien. Sur les deux "files", j'ai bien à chaque fois 2 nombres différents. Mais dès que je rajoute un deuxième thread, eh bien les nombres générés sont identiques (dans le thread a et b). A priori, ça me semble logique (vu que je lance une tache qui génère des nombres aléatoires, et rand() va générer dans les deux threads les même nombre).

    Bref, comment contourner mon problème ? de faire en sorte que, à partir de rand() :
    - threada => génère X
    - threadb => génère Y
    - fonction() => génère Z

    au lieu de

    - threada => génère X
    - threadb => génère X
    - fonction() => génère Z

    edit 1: Après réflexion, je me demandais s'il ne serait pas possible de rajouter à un argument à ma fonction generenb, qui serait une sorte de "grain de sel", argument que je changerais à chaque appele de ma fonction.

  2. #2
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par nuitn0ire Voir le message
    Lorsque je n'ai qu'un seul thread (le a), la fonction generenb() ainsi que mon thread fonctionnent bien. Sur les deux "files", j'ai bien à chaque fois 2 nombres différents. Mais dès que je rajoute un deuxième thread, eh bien les nombres générés sont identiques (dans le thread a et b). A priori, ça me semble logique (vu que je lance une tache qui génère des nombres aléatoires, et rand() va générer dans les deux threads les même nombre).
    C'est aussi ce que je constate avec ce code basique :
    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
     
    #include <windows.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
     
    void *task (void *puser)
    {
       int i;
       for (i = 0; i < 5; i++)
       {
          printf ("%s:%d\n", (char *) puser, rand ());
          Sleep (1);
       }
       return NULL;
    }
     
    int main (void)
    {
       srand (time (NULL));
       {
          pthread_t ta;
          pthread_t tb;
          pthread_create (&ta, NULL, task, (void *) "A");
          pthread_create (&tb, NULL, task, (void *) "B");
          pthread_join (ta, NULL);
          pthread_join (tb, NULL);
       }
       return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    A:41
    B:41
    A:18467
    B:18467
    A:6334
    B:6334
    A:26500
    B:26500
    A:19169
    B:19169
     
    Press ENTER to continue.
    C'est étrange. Il me semblait pourtant qu'il y a avait une statique pour rand() par procéssus, mais qu'il était commun aux tâches (threads).

    Ou pourrait effectivement démarrer chaque tâche avec un srand() différent...

    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
     
    #include <windows.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
     
    void *task (void *puser)
    {
       int i;
       char const *s = puser;
     
       srand (time (NULL) + s[0]);
     
       for (i = 0; i < 5; i++)
       {
          printf ("%s:%d\n", s, rand ());
          Sleep (1);
       }
       return NULL;
    }
     
    int main (void)
    {
       {
          pthread_t ta;
          pthread_t tb;
          pthread_create (&ta, NULL, task, (void *) "A");
          pthread_create (&tb, NULL, task, (void *) "B");
          pthread_join (ta, NULL);
          pthread_join (tb, NULL);
       }
       return 0;
    }
    Ca marche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    A:13289
    B:13293
    A:12938
    B:23687
    A:4678
    B:22542
    A:13224
    B:4519
    A:21361
    B:31676
     
    Press ENTER to continue.

  3. #3
    Membre confirmé
    Inscrit en
    Février 2008
    Messages
    67
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 67
    Par défaut
    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
    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
     
    #define NOMBRE 100
     
    char genere(int nombre);
     
    static void *task_a (void *p_data)
    {
     char const *s = p_data;
     srand (time (NULL) + s[0]);
     genere(NOMBRE);   
     (void) p_data;
     return NULL;
    }
     
    static void *task_b (void *p_data)
    {
     char const *s = p_data;
     srand (time (NULL) + s[0]);
     genere(NOMBRE);             
     (void) p_data;
     return NULL;
    }
     
    int main(int argc, char* argv[])
    {
     pthread_t ta;
     pthread_t tb;
     pthread_create (&ta, NULL, task_a, NULL);
     pthread_create (&tb, NULL, task_b, NULL);
     #if 1
     pthread_join (ta, NULL);
     pthread_join (tb, NULL);
     #endif
     
     system("PAUSE");    
     return 0;
    }
     
    char genere(int nombre)
    {
     int i; 
     for (i = 0 ; i < NOMBRE ; i++)
      {
      int max2 = 100;
      int min2 = 1;
      int nb
      nb=min2+(int) (rand() % (max2+1-min2));                            
     }
    }
    violent plantage chez moi

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Par défaut srand
    Oui, je crois savoir que le srand permet de generer des nombres aleatoires independants, pour chaque run du pgrm !
    rand ne fait que generer desnombres "pseudo-aleatoires" puisqu'ils seront tjs les memes apres chaque run ...


    (mais je crois que vous aviez trouve la soluce, deja)
    pepito

  5. #5
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par nuitn0ire Voir le message
    <...>violent plantage chez moi
    Bah, oui. J'avais pourtant donné un exemple qui fonctionne...
    • Il n'y a besoin que d'une fonction 'thread'
    • Il faut apprendre à utiliser le 4ème paramètre de pthread_create(). Si tu passes NULL tu reçois NULL dans la tâche, logique... Déréférencement de NULL -> comportement indéterminé, tout peut arriver.

    Ceci fonctionne. Si tu ne comprends pas poses des questions, mais ne recopies pas sans comprendre...
    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
     
    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
     
    #define NOMBRE 10
     
    void genere (int nombre, char const *s)
    {
       int i;
       for (i = 0; i < nombre; i++)
       {
          int max = 100;
          int min = 1;
          int nb;
          nb = min + (int) (rand () % (max + 1 - min));
          printf ("%s: nb=%d\n", s, nb);
       }
    }
     
    static void *task (void *p_data)
    {
       char const *s = p_data;
       srand (time (NULL) + s[0]);
       genere (NOMBRE, s);
       return NULL;
    }
     
    int main (void)
    {
       pthread_t ta;
       pthread_t tb;
       pthread_create (&ta, NULL, task, (void *) "A");
       pthread_create (&tb, NULL, task, (void *) "B");
       pthread_join (ta, NULL);
       pthread_join (tb, NULL);
     
       return 0;
    }
    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
     
    A: nb=75
    A: nb=90
    A: nb=16
    A: nb=40
    A: nb=40
    A: nb=80
    A: nb=35
    A: nb=40
    B: nb=78
    B: nb=70
    B: nb=12
    B: nb=35
    B: nb=55
    B: nb=6
    B: nb=47
    B: nb=13
    B: nb=76
    B: nb=51
    A: nb=32
    A: nb=99
     
    Press ENTER to continue.
    Comme il n'y a pas de suspension explicite, c'est sans doute printf() (fonction I/O standard) qui fait office de 'scheduler' quand il peut... Dans mon code, il y a vait un Sleep(1) qui garantissait une juste répartition du temps entre les 2 tâches.

  6. #6
    Membre confirmé
    Inscrit en
    Février 2008
    Messages
    67
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 67
    Par défaut
    Eh bien écoute, je ne comprends pas.. Pas envie de passer pour un boulet, mais voici mon code complet :

    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
    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
     
    #define NOMBRE 100
     
    char genereip(int nombreip);
     
    static void *task_a (void *p_data)
    {
     srand (time (NULL));
     genereip(NOMBRE);   
     (void) p_data;
     return NULL;
    }
     
    static void *task_b (void *p_data)
    {
     genereip(NOMBRE);             
     (void) p_data;
     return NULL;
    }
     
    int main(int argc, char* argv[])
    {
     pthread_t ta, tb;
     pthread_create (&ta, NULL, task_a, NULL);
     pthread_create (&tb, NULL, task_b, NULL);
     pthread_join (ta, NULL);
     pthread_join (tb, NULL);
     
     system("PAUSE");    
     return 0;
    }
     
    char genereip(int nombreip)
    {
     int i; 
     for (i = 0 ; i < NOMBRE ; i++)
      {
      int max2 = 1;
      int min2 = 100;
      int nb;
      nb=min2+(int) (rand() % (max2+1-min2));
      char ip[12];
      sprintf(ip,"192.168.1.%d",nb);
      printf("ip : %s\n",ip);
     }
    }
    Je comprends pas comment adapter ton code au mien. Par ailleurs, dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pthread_create (&ta, NULL, task, (void *) "A");
    je ne comprends pas le "A".

    (bon, je débute en C, mais j'aimerais bien comprendre comment réorganiser mon code de façon à corriger mon problème).

    Voilà. Patapay.

  7. #7
    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
    Emmanuel: Pour rand(), ça dépend des implémentations.
    La C Run-Time Library (CRT) de Microsoft utilise une "graine" par thread, pour rester thread-safe: Aucun thread n'a d'influence sur les autres.

    D'ailleurs, c'est malin, le coup du srand(time(NULL)+valeur spécifique au thread). Je vais le garder sous la main, ce truc...
    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.

  8. #8
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par nuitn0ire Voir le message
    Je comprends pas comment adapter ton code au mien.
    Déjà, il est incorrect :
    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
     
    Project   : Forums
    Compiler  : GNU GCC Compiler (called directly)
    Directory : C:\dev\forums\
    --------------------------------------------------------------------------------
    Switching to target: default
    Compiling: main.c
    main.c:25: warning: unused parameter 'argc'
    main.c:25: warning: unused parameter 'argv'
    main.c: In function `genereip':
    main.c:48: warning: too few arguments for format
    main.c: At top level:
    main.c:37: warning: unused parameter 'nombreip'
    main.c: In function `genereip':
    main.c:50: warning: control reaches end of non-void function
    Linking console executable: console.exe
    Process terminated with status 0 (0 minutes, 0 seconds)
    0 errors, 5 warnings
    Pourquoi tu crées 2 tâches. Les deux doivent avoir le même comportement, donc une seule suffit. Par contre, elles traiteront sans doute des données différentes, donc on passe un paramètre qui permet au moins de les différencier quand on fait un printf(), par exemple.
    est trop petit pour une adresse IP complète. Si tu ne sais pas compter (bienvenu au club!), tu fais comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          char ip[sizeof "255.255.255.255"];
    tu es alors sûr d"avoir la bonne taille.

    Tu as choisi une plage d'IP étrange...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      int max2 = 1;
      int min2 = 100;
    C'est voulu ?
    J'aurais dit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      int min = 1;
      int max = 100;
    et pourquoi se limiter à 100 ?
    Par ailleurs, dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pthread_create (&ta, NULL, task, (void *) "A");
    je ne comprends pas le "A".

    (bon, je débute en C, mais j'aimerais bien comprendre comment réorganiser mon code de façon à corriger mon problème).
    As-tu lu la doc de pthread_create () ? As-tu compris à quoi servait le 4ème paramètre ?

    http://emmanuel-delahaye.developpez.com/pthreads.htm

    notamment le chapitre sur les données...

    Je propose ceci :
    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
     
    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
     
    #include <windows.h>
     
    static char genereip (int nombreip, char const *s)
    {
       int i;
       for (i = 0; i < nombreip; i++)
       {
          int max = 254;
          int min = 2;
          int nb = min + (rand () % (max + 1 - min));
          char ip[sizeof "192.168.1.255"];
          sprintf (ip, "192.168.1.%d", nb);
          printf ("%s : ip : %s\n", s, ip);
          Sleep(1);
       }
    }
     
    static void *task (void *p_data)
    {
       char const *s = p_data;
     
       srand (time (NULL) + s[0]);
       genereip (100, s);
       return NULL;
    }
     
    int main (void)
    {
       pthread_t ta, tb;
       pthread_create (&ta, NULL, task, (void *) "alphonse");
       pthread_create (&tb, NULL, task, (void *) "beatrice");
       pthread_join (ta, NULL);
       pthread_join (tb, NULL);
     
       system ("PAUSE");
       return 0;
    }
    Pose des questions si tu ne comprends pas.

  9. #9
    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 Emmanuel Delahaye Voir le message
    C'est étrange. Il me semblait pourtant qu'il y a avait une statique pour rand() par procéssus, mais qu'il était commun aux tâches (threads).
    C'est quand même mieux d'avoir chaque thread qui peut appeler indépendament rand(), que d'avoir à protéger l'appel par un mutex!

    Citation Envoyé par Emmanuel Delahaye Voir le message
    Ou pourrait effectivement démarrer chaque tâche avec un srand() différent...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void *task (void *puser)
    {
       char const *s = puser;
     
       srand (time (NULL) + s[0]);
    ...
     
          pthread_create (&ta, NULL, task, (void *) "A");
          pthread_create (&tb, NULL, task, (void *) "B");
    Si les threads sont lancés suffisamment vite, alors ça marche.

    Si le "A" démarre seulement 1 seconde après (en réalité, dans la seconde suivante) le "B", ça marche plus. Le cas va rarement se présenter, mais il est tout à fait possible.

    Ce n'est donc pas une solution.

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

Discussions similaires

  1. Pthread et semaphores
    Par Yabo dans le forum Linux
    Réponses: 9
    Dernier message: 30/03/2008, 00h09
  2. Pb de rand() qui tourne en boucle
    Par MadChris dans le forum MFC
    Réponses: 3
    Dernier message: 26/06/2004, 16h24
  3. Probleme de tirage avec rand ?
    Par sunshine33 dans le forum MFC
    Réponses: 5
    Dernier message: 14/01/2004, 15h57
  4. rand
    Par drKzs dans le forum C
    Réponses: 6
    Dernier message: 21/09/2003, 16h39
  5. Réponses: 4
    Dernier message: 27/08/2003, 21h34

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