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 :

Pthreads: Conditions et Mutex


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 62
    Par défaut Pthreads: Conditions et Mutex
    Bonjour,

    J'aimerais connaître les avantages à utiliser les conditions en plus des mutex lors de l'utilisation de pthreads, les mutex permettent déjà d'assurer une certaine synchronisation entre les différents threads, pourquoi rajouter des conditions ?

  2. #2
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Salut,

    Comment peux-tu faire un producteur consommateur sans ces conditions ?

    Le principe est simple, tu as N threads d'un côté qui rangent des informations dans une liste et N threads de l'autre côté qui prennent ces informations.

    Si la liste est vide, alors les consommateurs doivent attendre..

    Si la liste est pleine, alors les producteurs doivent attendre...

    Comment faire attendre un thread ? pause() ? Non pas possible comment réveiller ce thread alors ? sleep(x) ? Pas très joli d'aller checker toutes les x secondes si un événement s'est passé...

    Pour t'en convaincre, la meilleure chose est d'implémenter l'exercice ci-dessus sans utiliser les conditions, on verra si tu y arrives

  3. #3
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 62
    Par défaut
    J'ai essayé de programmer l'exercice, bien que je doute que ce soit bon:
    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
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
     
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t	cond  = PTHREAD_COND_INITIALIZER;
     
    int	nbElements = 0;
     
    void * production(void * arg);
    void * consommation(void * arg);
     
    int main(int argc, char ** argv){
    	pthread_t	prod;
    	pthread_t	conso;
    	char	*liste = malloc(20);
    	int		i;
     
    	for (i = 0; i < 5; i++) {
    		if(pthread_create(& conso, NULL, consommation, (void *)&liste) != 0){
    			perror("pthread_create");
    			exit(1);
    		}
     
    		if(pthread_create(& prod, NULL, production, (void *)&liste) != 0){
    			perror("pthread_create");
    			exit(1);
    		}
    	}
     
    	pthread_exit(NULL);
     
    	return 0;
    }
     
    void * production(void * arg){
    	char	*liste = (char *)arg;
    	while(1){
    		pthread_mutex_lock(& mutex);
     
    		if(nbElements < 20){
    			liste[nbElements] = 'a';
    			nbElements++;
    			printf("Production de l'element 'a', nbElement vaut %d\n", nbElements); 
    		}
     
    		//pthread_cond_signal(& cond);
    		pthread_mutex_unlock(& mutex);
    	}
    }
     
    void * consommation(void * arg){
    	char	*liste = (char *)arg;
     
    	while(1){
    		pthread_mutex_lock(& mutex);
    		//pthread_cond_wait(& cond, & mutex);
    		if(nbElements > 0){
    			nbElements--;
    			printf("Consommation de l'element 'a', nbElement vaut %d\n", nbElements); 
    		}
     
    		pthread_mutex_unlock(& mutex);
    	}
    }
    Je comprends pas vraiment les résultats d'ailleurs, si je commente les conditions, j'obtiens:
    ...
    Production de l'element 'a', nbElement vaut 1
    Production de l'element 'a', nbElement vaut 2
    Production de l'element 'a', nbElement vaut 3
    Production de l'element 'a', nbElement vaut 4
    Production de l'element 'a', nbElement vaut 5
    Consommation de l'element 'a', nbElement vaut 4
    Consommation de l'element 'a', nbElement vaut 3
    Consommation de l'element 'a', nbElement vaut 2
    Consommation de l'element 'a', nbElement vaut 1
    Consommation de l'element 'a', nbElement vaut 0
    Production de l'element 'a', nbElement vaut 1
    Production de l'element 'a', nbElement vaut 2
    Production de l'element 'a', nbElement vaut 3
    Production de l'element 'a', nbElement vaut 4
    Production de l'element 'a', nbElement vaut 5
    Production de l'element 'a', nbElement vaut 6
    Production de l'element 'a', nbElement vaut 7
    Production de l'element 'a', nbElement vaut 8
    Production de l'element 'a', nbElement vaut 9
    Production de l'element 'a', nbElement vaut 10
    Production de l'element 'a', nbElement vaut 11
    Consommation de l'element 'a', nbElement vaut 10
    Consommation de l'element 'a', nbElement vaut 9
    Consommation de l'element 'a', nbElement vaut 8
    Consommation de l'element 'a', nbElement vaut 7
    Consommation de l'element 'a', nbElement vaut 6
    Consommation de l'element 'a', nbElement vaut 5
    Les threads semblent se partager les tâches de facon aléatoire, j'aurais cru qu'il y avait une sorte de pile avec tous les threads en attentes, ou les threads en attentes sont triés par ordre dans lequel ils ont demandé à accéder au mutex, mais ça n'a pas l'air d'être le cas

    Si j'utilise les conditions, j'ai:
    ...
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Consommation de l'element 'a', nbElement vaut 18
    Production de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Consommation de l'element 'a', nbElement vaut 18
    Consommation de l'element 'a', nbElement vaut 17
    Production de l'element 'a', nbElement vaut 18
    Production de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Consommation de l'element 'a', nbElement vaut 18
    Production de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Consommation de l'element 'a', nbElement vaut 19
    Consommation de l'element 'a', nbElement vaut 18
    Consommation de l'element 'a', nbElement vaut 17
    Encore une fois les résultats semblent aléatoires, alors soit je sais pas les utiliser ( ce qui est tout à fait possible ), soit j'ai pas compris comment les mutex/conditions fonctionnent..

  4. #4
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Il y a en effet une pile mais imagine ta pile :
    C : Consomateur
    P : Producteur

    Element restant : 1
    C - C - C - C - C - P - P - P - P - P

    //consommation

    Element restant : 0
    C - C - C - C - P - P - P - P - P

    //aucune consommation

    Element restant : 0
    C - C - C - P - P - P - P - P

    //aucune consommation

    Element restant : 0
    C - C - P - P - P - P - P

    //aucune consommation

    Element restant : 0
    C - P - P - P - P - P

    //aucune consommation

    Element restant : 0
    P - P - P - P - P

    //production

    Element restant : 1
    P - P - P - P


    Tu peux donc avoir plusieurs productions consécutives et y avoir quelques consommateurs qui ont accès au mutex mais qui le rendent sans consommer.

    Il faut aussi bien penser que tes threads doivent aussi se partager le CPU. Des threads sont donc "endormis" en attente de recevoir le CPU.
    Un thread peut donc être "endormis" en dehors de la zone critique pendant qu'un autre thread passe 2 fois sur la zone critique.

  5. #5
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 62
    Par défaut
    A priori, avec le code du dessus ( sans les conditions ), je devrais donc avoir:

    C - P - C - P - C - P - C - P - C - P

    or j'obtiens au début de l’exécution:

    Production de l'element 'a', nbElement vaut 1
    Production de l'element 'a', nbElement vaut 2
    Production de l'element 'a', nbElement vaut 3
    Consommation de l'element 'a', nbElement vaut 2
    Consommation de l'element 'a', nbElement vaut 1
    Production de l'element 'a', nbElement vaut 2
    Production de l'element 'a', nbElement vaut 3
    Consommation de l'element 'a', nbElement vaut 2
    Consommation de l'element 'a', nbElement vaut 1
    Consommation de l'element 'a', nbElement vaut 0
    Production de l'element 'a', nbElement vaut 1
    Production de l'element 'a', nbElement vaut 2
    Production de l'element 'a', nbElement vaut 3
    Production de l'element 'a', nbElement vaut 4
    Production de l'element 'a', nbElement vaut 5
    Production de l'element 'a', nbElement vaut 6
    Production de l'element 'a', nbElement vaut 7
    Production de l'element 'a', nbElement vaut 8
    Production de l'element 'a', nbElement vaut 9
    Production de l'element 'a', nbElement vaut 10
    Production de l'element 'a', nbElement vaut 11
    Production de l'element 'a', nbElement vaut 12
    Production de l'element 'a', nbElement vaut 13
    Production de l'element 'a', nbElement vaut 14
    Production de l'element 'a', nbElement vaut 15
    Production de l'element 'a', nbElement vaut 16
    Production de l'element 'a', nbElement vaut 17
    Production de l'element 'a', nbElement vaut 18
    Production de l'element 'a', nbElement vaut 19
    Production de l'element 'a', nbElement vaut 20
    Même si le premier C ne fait rien, le deuxieme devrais consommer ce que le premier P a produit, or c'est pas le cas ici

  6. #6
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2009
    Messages : 62
    Par défaut
    Après modification:
    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
    void * production(void * arg){
    	char	*liste = (char *)arg;
     
    	while(1){
    		pthread_mutex_lock(& mutex);
     
    		while(nbElements >= 20)
    			pthread_cond_wait(& non_plein, & mutex); // Les producteurs attendent que le tableau ne soit plus plein non ?
     
    		liste[nbElements] = 'a';
    		nbElements++;
    		printf("Production de l'element 'a', nbElement vaut %d\n", nbElements); 
     
    		pthread_cond_signal(&non_vide); // De même, ils envoient un signal si il y a qqch à consommer
     
    		pthread_mutex_unlock(& mutex);
    	}
    }
     
    void * consommation(void * arg){
    	char	*liste = (char *)arg;
     
    	while(1){
    		pthread_mutex_lock(& mutex);
     
    		while(nbElements <= 0)
    			pthread_cond_wait(& non_vide, & mutex);
     
    		nbElements--;
    		printf("Consommation de l'element 'a', nbElement vaut %d\n", nbElements); 
    		sleep(1);
     
    		pthread_cond_signal(& non_plein);
    		pthread_mutex_unlock(& mutex);
    	}
    }
    En fait quand je disais une liste, c'était au sens structure de donnée, mais il se peut que tu ne connaisses pas. Le problème avec ton tableau, c'est que les éléments insérés en premier ne sont pas sorti en premier.. C'est le principe d'une pile (LIFO) et non d'une file (FIFO). Ça dépasse le cadre du sujet donc je te laisse te renseigner.
    Ha effectivement, j'avais compris tableau, je vois ce que c'est les listes, cela dit, du fait que tous les éléments soit des 'a' ici, c'est peut être pas si génant dans le contexte actuel, bien que je comprenne que dans un autre contexte ce soit nécessaire.

    Je pense saisir l'utilité des conditions, sans elles on a, dans le cas ou la liste est pleine, un truc du genre (pour la partie producteurs):

    P1 - P2 - P3 - P4 - P5 - P1 - P2 - P3 etc

    Au lieu d'avoir

    P1 - P2 - P3 - P4 - P5 tout court, ou les 5 producteurs sont endormis

    La question à laquelle je t'invite à réfléchir c'est : "Pourquoi est-ce que c'est obligatoire d'utiliser while(nbElements >= 20) pthread_cond_wait(&mutex, &non_vide); plutôt que if(nbElements >= 20) pthread_cond_wait(&mutex, &non_vide); ?". Essaye de trouver un cas où c'est obligatoire.
    Du fait que pthread_cond_wait relâche le mutex, on peut avoir plusieurs producteurs en attente. Lors de la réception du signal émis par un consommateur, si on a un if, chacun des producteurs en attente reprend, si le consommateur a envoyé un signal et nbElements vaut 19, mais que 3 producteurs étaient en attente, on aura nbElements > 20, ce qui risque de provoquer une erreur de segmentation.

    Toutefois, si ce que je viens d'écrire est vrai, je ne suis pas sur de saisir en quoi un while changerait quelque chose, si on a 3 producteurs qui reçoivent le signal du consommateur, ils pourraient très bien sortir de la boucle avant qu'un des 2 autres producteurs ai le temps de re-incrementer nbElements pour le rendre de nouveau >= 20, auquel cas on aurait quand meme nbElements > 20 à un moment ou à un autre

  7. #7
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Alors une petite explication s'impose

    Tout d'abord, tu dis :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Les threads semblent se partager les tâches de facon aléatoire, j'aurais cru qu'il y avait une sorte de pile avec tous les threads en attentes, ou les threads en attentes sont triés par ordre dans lequel ils ont demandé à accéder au mutex, mais ça n'a pas l'air d'être le cas.
    Comment peux-tu avancer ça alors que tu n'affiches pas l'ID des threads ? L'accès au mutex est réglementé par une file, c'est-à-dire que le premier entré sera le premier sorti (encore heureux sinon certain thread risquerait de rester au fond et de ne jamais y aller). Maintenant qui arrive le premier sur le mutex, et bien tu ne peux pas le savoir, en fait juste avant que le thread 1 arrive sur le mutex, il se peut qu'il y ait interruption et que le thread 2 y arrive sans interruption (et dans ce cas il aura "dépassé" le thread 1).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char	*liste = malloc(20);
    En fait quand je disais une liste, c'était au sens structure de donnée, mais il se peut que tu ne connaisses pas. Le problème avec ton tableau, c'est que les éléments insérés en premier ne sont pas sorti en premier.. C'est le principe d'une pile (LIFO) et non d'une file (FIFO). Ça dépasse le cadre du sujet donc je te laisse te renseigner.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	while(1){
    		pthread_mutex_lock(& mutex);
     
    		if(nbElements < 20){
    			liste[nbElements] = 'a';
    			nbElements++;
    			printf("Production de l'element 'a', nbElement vaut %d\n", nbElements); 
    		}
     
    		//pthread_cond_signal(& cond);
    		pthread_mutex_unlock(& mutex);
    	}
    Voici notre producteur, en effet sans la condition ça marche mais tu obtiens alors une boucle active. C'est-à-dire que le thread utilise inutilement des ressources car tourne en boucle...

    Imagine simplement que tu aies 20 producteurs et un consommateur, et encore ce consommateur va consommer une fois toute les 5 minutes... Tes producteurs vont prendre le mutex et le relâcher tout de suite, un à la suite de l'autre, et sans arrêt pendant 5 minutes ! Ce qui, tu comprendras, est du gaspillage pure et simple de ressources.

    En ajoutant le pthread_cond_signal(& cond);, que se passe t'il ? Avant de répondre, il faut corriger ton code car il est manque deux instructions.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    	while(1){
    		pthread_mutex_lock(& mutex);
     
                    while(nbElements >= 20)
                      pthread_cond_wait(&mutex, &non_vide);
     
    	        liste[nbElements] = 'a';
    		nbElements++;
    		printf("Production de l'element 'a', nbElement vaut %d\n", nbElements); 
     
                    pthread_cond_signal(&non_plein);
     
    		pthread_mutex_unlock(& mutex);
    	}
    Tu commences peut-être à comprendre l'intérêt. Si le producteur qui est dans la section critique (c'est-à-dire entre un lock et unlock) ne peut produire car le tableau est plein, il suffit qu'il attende qu'un consommateur lui signale qu'il a consommé.

    Une fois qu'il a produit alors il signale au consommateur (qui attende peut-être car le tableau est vide) que le tableau n'est plus vide.

    Il faut donc utiliser deux conditions, une pour signaler que ce n'est plus plein, et une autre pour signaler que ce n'est plus vide.

    Il faut également comprendre que lorsque qu'un thread entre dans un pthread_cond_wait, il libère le mutex ! Ce qui fait qu'un autre producteur peut entrer en section critique et voir également que c'est plein et tomber lui aussi dans le pthread_cond_wait. Ils attendront alors tout les deux.


    La question à laquelle je t'invite à réfléchir c'est : "Pourquoi est-ce que c'est obligatoire d'utiliser while(nbElements >= 20) pthread_cond_wait(&mutex, &non_vide); plutôt que if(nbElements >= 20) pthread_cond_wait(&mutex, &non_vide); ?". Essaye de trouver un cas où c'est obligatoire.

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

Discussions similaires

  1. Réponses: 7
    Dernier message: 27/08/2007, 08h28
  2. Exclusion mutuelle & Conditions Pthreads
    Par DJ@M's dans le forum C
    Réponses: 3
    Dernier message: 14/01/2006, 18h07
  3. Mutex et condition
    Par Vodlich dans le forum C
    Réponses: 10
    Dernier message: 14/12/2005, 22h09
  4. pthread, mutex
    Par ELKCHAOU dans le forum MFC
    Réponses: 5
    Dernier message: 14/07/2005, 20h46
  5. pthread et mutex
    Par Nico65 dans le forum C++
    Réponses: 20
    Dernier message: 16/01/2005, 12h30

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