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 :

[Pthread] Utilisation de pthread_create


Sujet :

C

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    40
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 40
    Par défaut [Pthread] Utilisation de pthread_create
    Bonjour,
    je cherche à mieux comprendre le bout de code (venant d'un livre) que voici:
    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
    #include <stdio.h>
    #include <pthread.h>
     
    int i;
     
    void addition(void)
    {
    	i = i + 10;
    	printf("hello thread fils i=%d\n", i);
    	i = i + 20;
    	printf("hello thread fils i=%d\n", i);
    }
     
    int main(void)
    {
    	pthread_t num_thread;
    	i = 0;
    	if(pthread_create(&num_thread, NULL, (void *(*)())addition, NULL) == -1) perror("pb pthread_create\n");
    	i = i + 1000;
    	printf("thread principal %d\n", i);
    	i = i + 2000;
    	printf("thread principal %d\n", i);
    	pthread_join(num_thread, NULL);
    }
    si je comprend bien sur la fonction pthread_create on a en deuxième paramètre un pointeur de fonction... mais pourquoi (void *(*)()) devant?
    je n'arrive pas à décortiquer

    merci bien

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 453
    Par défaut
    Bonjour,

    Il s'agit d'un transtypage (cast), d'un pointeur de fonction quelconque vers quelque chose qui correspond au prototype de pthread_create(). L'ennui, c'est que l'exemple tiré de ton livre est faux (et ce n'est pas une faute de frappe puisque la fonction addition ne retourne rien alors qu'elle devrait renvoyer un pointeur, fût-il NULL). Le troisième argument de pthread_create() attend un pointeur de fonction de la forme « void * fonction (void *) », autrement dit une fonction pouvant recevoir un pointeur void et retournant un autre pointeur void.

    En partant du centre vers l'extérieur, il faut lire :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    *                   // Pointeur
    (*)                 // Parenthèses nécessaires pour éviter une ambiguïté
    (*)()               // Parenthèses suivant l'expression, indiquant qu'il s'agit d'une fonction, et donc ici d'un pointeur de fonction
    (*)(void *)         // Ces parenthèses contiennent en tout et pour tout un seul paramètre : un pointeur void *
    void *(*)(void *)   // L'expression entière a un type : void *. C'est donc le type de la fonction proprement dite. Autrement dit, celui de la valeur retournée
    (void *(*)(void *)) // Parenthèses autour de l'expression entière qui elle-même définit un type. Donc un transtypage.

    Le concept derrière tout cela est que, comme les données à recevoir et à renvoyer dépendent de la fonction proprement dite, il n'est pas possible d'imposer un prototype universel pour toutes les fonctions lancées par pthread_create(). On va donc passer un pointeur référençant une donnée unique ou une structure contenant toutes les informations à échanger, et la fonction va faire de même en retour.

    Il faudra donc transtyper ce pointeur à l'usage mais, en pratique, on fait l'inverse : on définit clairement le type à utiliser dans le prototype de la fonction, et on transtype le pointeur quand on le passe à pthread_create() pour qu'il correspondent à son prototype.

  3. #3
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Le beau comportement indéfini que voilà ! Ce programme est erroné (et je passe sur le fait qu'on join même si la création du thread a échoué..) et si c'est le cas pour le reste du livre je te recommanderais de changer de crèmerie.

    La bonne manière de faire est d'écrire un wrapper :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void *addition_wrap(void *unused) { addition(); return NULL; }

  4. #4
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    void *addition_wrap(void *unused) { addition(); }
    il ne faudrait pas un return NULL pour s'éviter un warning ou une erreur selon les options de compilation?

  5. #5
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Si, bien entendu. Corrigé !

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Il faudra donc transtyper ce pointeur à l'usage mais, en pratique, on fait l'inverse : on définit clairement le type à utiliser dans le prototype de la fonction, et on transtype le pointeur quand on le passe à pthread_create() pour qu'il correspondent à son prototype.
    Peux-tu nous montrer un exemple de code ?

    Perso, quand j'ai ce genre de pointeur void* en paramètre, je le caste à l'intérieur de la fonction qui s'en sert. Et je documente la fonction pour dire que le void* doit en fait être un toto*. Mais il y a peut-être plus élégant d'après ce que tu dis.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 453
    Par défaut
    Hello,

    Citation Envoyé par Bktero Voir le message
    Perso, quand j'ai ce genre de pointeur void* en paramètre, je le caste à l'intérieur de la fonction qui s'en sert. Et je documente la fonction pour dire que le void* doit en fait être un toto*. Mais il y a peut-être plus élégant d'après ce que tu dis.
    À vrai dire, les deux se valent, en fonction du contexte et des conventions adoptées au sein d'une équipe.

    En fait, je pensais surtout aux fonctions de callback en général. C'est ce dont il s'agit dans le cas des pthreads, mais l'approche est la même avec les toolkits graphiques en tous genres, comme GTK, dans lesquels on associe une fonction avec un objet, comme un bouton. Comme on passe généralement toute l'information associée via un pointeur void *, j'ai plutôt tendance à écrire la fonction telle qu'elle devrait l'être si elle devait être appelée directement par l'utilisateur, pour garantir les types et pour permettre aussi la documentation auto-générée dans une certaine mesure.

    En plus, dans le cas contraire, si on fait massivement usage de la donnée transmise, par exemple si c'est un pointeur vers une structure qui contient tous les paramètres, alors il vaut mieux déclarer un pointeur correctement typé en début de fonction et lui affecter la valeur du pointeur void * passé en paramètre plutôt que transtyper le paramètre lui-même à toutes les lignes de la fonction auxquelles il est utilisé.

    Mais il est de fait que si la fonction n'est faite que pour être exploitée qu'à travers une seule interface (ce qui est le cas des pthreads, là encore), alors c'est aussi bien, effectivement, de garder le même prototype pour toute une famille de fonction et de limiter les casts au sein des appels eux-mêmes.

    Le C est fait pour être permissif et il ne faut pas s'en priver. L'essentiel est de garder un code clair et intuitif pour un nouveau venu sur un projet, quelque soit la ligne adoptée.

  8. #8
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Permissif, il y a des limites. Ce type de cast est illégal, j'insiste, c'est du comportement indéfini pur jus.

  9. #9
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 453
    Par défaut
    Bonjour,

    Citation Envoyé par Matt_Houston Voir le message
    Permissif, il y a des limites. Ce type de cast est illégal, j'insiste, c'est du comportement indéfini pur jus.
    Pourquoi « illégal » ?

    Et que faire d'autre que l'une de ces deux approches quand le prototype de la fonction appelante (ici pthread_create) est construit comme cela ?

  10. #10
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Je précise que je cause bien de cette ligne du programme de l'OP :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	if(pthread_create(&num_thread, NULL, (void *(*)())addition, NULL) == -1) perror("pb pthread_create\n");
    Ce n'est pas autorisé car pthread tentera d'invoquer addition en déréférençant donc le pointeur vers un type autre que son type d'origine (cf. section 6.3.2.3.8 de la norme ISO : If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.).

    La solution est d'écrire un wrapper, comme je l'ai mentionné.

  11. #11
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    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 393
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    À vrai dire, les deux se valent,
    Absolument pas!
    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.

  12. #12
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 453
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Je précise que je cause bien de cette ligne du programme de l'OP :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	if(pthread_create(&num_thread, NULL, (void *(*)())addition, NULL) == -1) perror("pb pthread_create\n");
    Ce n'est pas autorisé car pthread tentera d'invoquer addition en déréférençant donc le pointeur vers un type autre que son type d'origine (cf. section 6.3.2.3.8 de la norme ISO : If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.).
    Citation Envoyé par Médinoc Voir le message
    Absolument pas!
    Vous avez raison, en effet. J'ai posté un peu trop vite.
    Je m'étais focalisé sur l'argument passé sans tenir compte du cast de la valeur de retour, car j'avais à l'esprit certaines fonctions utilisés comme des procédures et de type void (sans pointeur).

Discussions similaires

  1. Aide pthread utilisation
    Par mohaskh dans le forum C
    Réponses: 9
    Dernier message: 26/11/2014, 15h39
  2. Utilisation de pthread_create avec plusieurs arguments
    Par Red Sno dans le forum Bibliothèques, systèmes et outils
    Réponses: 5
    Dernier message: 11/12/2012, 18h01
  3. Utilisation de pthread et structure
    Par Nieli dans le forum C
    Réponses: 1
    Dernier message: 10/10/2008, 20h47
  4. utilisation d'un timer avec les pthreads
    Par dc.sara dans le forum C
    Réponses: 8
    Dernier message: 15/01/2008, 13h12
  5. [Visual C++] Utilisation de pthread
    Par Stupeflip dans le forum Windows
    Réponses: 4
    Dernier message: 23/02/2007, 14h36

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