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

  1. #1
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    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 expérimenté 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
    Points : 1 396
    Points
    1 396
    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
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    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 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    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 012
    Points : 23 145
    Points
    23 145
    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 expérimenté 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
    Points : 1 396
    Points
    1 396
    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.

  6. #6
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    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

  7. #7
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    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

  8. #8
    Membre expérimenté 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
    Points : 1 396
    Points
    1 396
    Par défaut
    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.
    C'est bien ça.

    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
    Non car n'oublions pas qu'on est en section critique, donc un thread à la fois tout de même ! Si nbElement = 20 et que la séquence est P P P C alors seulement un producteur va pouvoir sortir mais les autres seront tout de même en quelque sorte "débloqué" de la condition et attendrons que le mutex soit libéré. Ainsi, si on a P P P C P P, le dernier producteur verra que nbElement = 20 et ira se ré-endormir en attendant un prochain signal.

    L'astuce, c'est que ce n'est pas assuré que signal ne réveille que un thread, donc tu peux considérer que signal == broadcast. Si on étais sur que signal ne réveille que un thread alors le while serait inutile

  9. #9
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    Par défaut
    D'accord, toutefois, je ne comprends pas ce résultat d'execution:
    Production de l'element 'a', nbElement vaut 1
    Production 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
    Consommation de l'element 'a', nbElement vaut 19
    Consommation de l'element 'a', nbElement vaut 18
    Consommation de l'element 'a', nbElement vaut 17
    Consommation de l'element 'a', nbElement vaut 16
    Consommation de l'element 'a', nbElement vaut 15
    Consommation de l'element 'a', nbElement vaut 14
    Consommation de l'element 'a', nbElement vaut 13
    Consommation de l'element 'a', nbElement vaut 12
    Consommation de l'element 'a', nbElement vaut 11
    Consommation de l'element 'a', nbElement vaut 10
    Comment la production peut-elle produire 20 élément d'un coup sans qu'un seul consommateur agisse alors qu'il y a 5 producteurs et 5 consommateurs ?
    De même, les consommateurs consomment tout ce qui a été produit avant de relancer la production, or des signaux sont envoyés a chaque production/consommation d'un élément

  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    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 012
    Points : 23 145
    Points
    23 145
    Par défaut
    N'oublie pas ce que j'ai dit quelques posts plus haut.
    L'ordonnanceur du CPU joue lui aussi un rôle.

    Les consommateur peuvent ne pas avoir accès au CPU lorsqu'ils sont en dehors du chemin critique. Un producteur qui a accès au CPU pendant un certains temps peut très bien pouvoir faire plusieurs tours avant qu'il doive rendre l'accès au CPU.

  11. #11
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par Neckara Voir le message
    N'oublie pas ce que j'ai dit quelques posts plus haut.
    L'ordonnanceur du CPU joue lui aussi un rôle.

    Les consommateur peuvent ne pas avoir accès au CPU lorsqu'ils sont en dehors du chemin critique. Un producteur qui a accès au CPU pendant un certains temps peut très bien pouvoir faire plusieurs tours avant qu'il doive rendre l'accès au CPU.
    Dans ce cas pourquoi en ajoutant les instructions pthread_yield() j'ai encore les mêmes résultats ?

    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
    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("%lu : Production de l'element 'a', nbElement vaut %d\n", pthread_self(), nbElements); 
     
    		pthread_cond_signal(&non_vide); // De même, ils envoient un signal si il y a qqch à consommer
    		pthread_mutex_unlock(& mutex);
    		pthread_yield();
     
    	}
    }
     
    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("%lu : Consommation de l'element 'a', nbElement vaut %d\n", pthread_self(), nbElements); 
    		sleep(1);
     
    		pthread_cond_signal(& non_plein);
    		pthread_mutex_unlock(& mutex);
    		pthread_yield();
    	}
    }
    Mais même si ils ont le temps d'effectuer plusieurs tours d'un coup, lors du relâchement du mutex, quelqu'un d'autre le prend, du coup lors du second tour le producteurs devrait bloquer sur l'instruction pthread_mutex_lock() non ?

  12. #12
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    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 012
    Points : 23 145
    Points
    23 145
    Par défaut
    En regardant vite fait le man, on voit que pthread_yield n'est pas standard et, sous Linux, n'est qu'un appel à la fonction sched_yield qui elle est standard (donc il vaut mieux l'utiliser directement).


    Sinon plutôt que de n'afficher que les consommations et productions, pourquoi ne pas afficher :
    "le thread n°i (Consommateur) entre dans la zone critique"
    et
    "le thread n°i (Consommateur) sort de la zone critique" ?

    Ce sera plus parlant.

    Sinon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    	pthread_mutex_unlock(& mutex);
    	pthread_yield();
    Le thread peut rendre le CPU entre les deux lignes.
    Quand il reprendra l'accès au CPU, il devra à nouveau directement le rendre.

  13. #13
    Membre expérimenté 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
    Points : 1 396
    Points
    1 396
    Par défaut
    Comment la production peut-elle produire 20 élément d'un coup sans qu'un seul consommateur agisse alors qu'il y a 5 producteurs et 5 consommateurs ?
    De même, les consommateurs consomment tout ce qui a été produit avant de relancer la production, or des signaux sont envoyés a chaque production/consommation d'un élément
    En même temps c'est normal, et je suis prêt à parier que c'est le même thread producteur qui produit tout... C'est parce que ça ne prend pas beaucoup de temps alors il garde la main tout ce temps.

    Ensuite, si tu veux des résultats plus cohérents, il faudrait que tu fasses comme si la production d'un élément prenait un certain temps et que sa consommation prenait également un certain temps. Évidemment il faut faire ça en dehors de la section critique. Tu peux utiliser des sleep par exemple...
    Mais même si ils ont le temps d'effectuer plusieurs tours d'un coup, lors du relâchement du mutex, quelqu'un d'autre le prend, du coup lors du second tour le producteurs devrait bloquer sur l'instruction pthread_mutex_lock() non ?
    Non car si le thread a encore du temps, il peut très bien aller le reprendre, et ce, plusieurs fois. Et même lorsqu'il est interrompu, un autre producteur peut encore faire de même.

  14. #14
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    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 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Tu ne dois pas t'intéresser ou supposer l'ordre d'exécution des thread en dehors de la zone critique.

    Tu dois partir du principe que s'il y a X threads en dehors de la zone critique, n'importe lequel de ces threads pourra prendre le mutex.

    Je ne me rappelle plus très bien des règles avec les mutex :
    - aucune supposition ne doit être fait sur la vitesse d'exécution de chacun des thread
    - un thread ne doit pas passer trop de temps dans la zone critique
    - un thread ne doit pas attendre trop de temps pour entrer dans la zone critique (la notion de temps étant relative bien sûr)

  15. #15
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Non car si le thread a encore du temps, il peut très bien aller le reprendre, et ce, plusieurs fois. Et même lorsqu'il est interrompu, un autre producteur peut encore faire de même.
    C'est ca que je ne comprends pas, si on a C0 (par exemple) qui est déjà en attente pour prendre le mutex, qui est lui même pris par P1, lorsque P1 va le relacher, ce n'est pas C0 qui va le reprendre mais P1 (dans le cas ou il lui reste du temps CPU) ? P1 ne va pas être placé en file d'attente, bloquer jusqu'a rendre le CPU, puis C0 prendra le mutex ?

    Dans ce cas est-ce vraiment une FIFO ?
    Au lieu d'avoir:
    P1 in - 1ere position
    C0 in - 2e
    P1 out - C0 passe en 1e
    P1 in - 2e
    C0 out - Sort avant P1 car first in
    P1 out

    On a:
    P1 in - 1e
    C0 in - 2e
    P1 out - C0 devrais passer en 1e non ?
    P1 in - P1 toujours premier lorsqu'il revient dans la file d'attente ?
    P1 out
    ...
    C0 out

    C'est effectivement P1 en l'occurence qui produit tout :
    C0 rentre en zone critique
    P0 rentre en zone critique
    P0 : Production de l'element 'a', nbElement vaut 1
    C0 : Consommation de l'element 'a', nbElement vaut 0
    P0 sort de la zone critique
    C0 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 1
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 2
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 3
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 4
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 5
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 6
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 7
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 8
    P1 sort de la zone critique
    P1 rentre en zone critique
    P1 : Production de l'element 'a', nbElement vaut 9
    P1 sort de la zone critique
    C0 rentre en zone critique
    C0 : Consommation de l'element 'a', nbElement vaut 8
    C0 sort de la zone critique
    C2 rentre en zone critique
    C2 : Consommation de l'element 'a', nbElement vaut 7
    C2 sort de la zone critique
    C1 rentre en zone critique
    C1 : Consommation de l'element 'a', nbElement vaut 6

  16. #16
    Membre expérimenté 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
    Points : 1 396
    Points
    1 396
    Par défaut
    Alors je me suis avancé trop vite en disant que c'était une file. Il n'y a peut-être pas d'ordre spécifique.

    J'ai essayé de regarder dans l'implémentation mais j'avoue que je ne comprend pas vraiment : http://www.koders.com/c/fid49461D80E...62FD28FCA.aspx

    Si quelqu'un sait, ça m'intéresse également

    EDIT : voir message plus bas.

  17. #17
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    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 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Je suis déjà sûr que c'est une liste.

    La logique voudrait que ce soit une FIFO (et il me semble bien que c'est le cas, je regarderais mes cours ce soir).

    Sinon ça n'a pas de sens, si il y a à chaque instant t au moins un thread attendant l'accès à la zone critique alors le premier thread à être bloqué n'aura jamais l'accès à la zone critique ?

    Il ne faut pas aussi faire dire à tes résultats ce qu'ils ne veulent pas dire.
    Tu sais juste quel thread entre et sort de ta zone critique mais rien de plus.

    Il est (il me semble) impossible de déterminer de façon sûre l'ordre dans lequel les threads demandent l'accès à la zone critique.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    while(nbElements >= 20)
    			pthread_cond_wait(& non_plein, & mutex);
    Il ne faut pas faire des choses comme cela.
    Dans ce cas là il faut utiliser deux sémaphores avec un nombre de jeton de 20.
    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
     
    //consomateur
    Prendre 1 jeton A //consommation
     
    Prendre mutex;
     
    //phase critique
     
    Rendre mutex;
     
    Rendre 1 jeton B //on peut produire un de plus
     
     
     
     
    //producteur
    Prendre 1 jeton B //production
     
    Prendre mutex;
     
    //phase critique
     
    Rendre mutex;
     
    Rendre 1 jeton A //on peut consommer un de plus

  18. #18
    Membre expérimenté 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
    Points : 1 396
    Points
    1 396
    Par défaut
    Il est (il me semble) impossible de déterminer de façon sûre l'ordre dans lequel les threads demandent l'accès à la zone critique.
    L'accès à mon avis non, mais par contre lorsqu'il rentre dans la zone critique je dirai que oui, et si c'est une FIFO alors ça revient au même.

    Par contre je viens d'avoir un tilt suite au commentaire de Neckara.

    En effet, ta séquence semblait étrange Arkenis, mais regardons de plus près :

    P1 in - 1e
    C0 in - 2e
    P1 out - C0 devrais passer en 1e non ?
    P1 in - P1 toujours premier lorsqu'il revient dans la file d'attente ?
    P1 out
    P1 in : on peut effectivement supposé si on a le printf.
    C0 in : Là c'est une supposition impossible, en effet, C0 pourrait très ne pas encore avoir demandé le mutex alors que P1 a déjà fait "un tour"

    Mystère résolu et à mon avis c'est bien une FIFO finalement

  19. #19
    Nouveau membre du Club
    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
    Points : 36
    Points
    36
    Par défaut
    Okay, faut que je me fasse au raisonnement multi-thread/processus, je pensais pas que P1 aurait le temps de faire 20 tour avant même que C0 demande le mutex, mais en y réfléchissant, si il peut faire autant de tour en un cycle cpu, ça semble logique

    Résolu !

    Merci à vous deux

  20. #20
    Futur Membre du Club
    Inscrit en
    Mai 2007
    Messages
    12
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 12
    Points : 9
    Points
    9
    Par défaut une autre solution avec les sémaphores posix
    une autre solution avec les sémaphores posix

    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <semaphore.h>
    #include <pthread.h>
     
    sem_t pleine;
    sem_t vide;
    sem_t mutex;
     
    void * production(void * arg);
    void * consommation(void * arg);
     
    int   indexe=0;
    char liste_produits [20]=" ";//zone de 20 produits
     
    int main(void)
    {
     
    	pthread_t	prod[20];
    	pthread_t	conso[20];
    	int	i,tirage, nbr_prod=0, nbr_cons=0;
     
     
        sem_init(&pleine, 0, 0);
        sem_init(&vide, 0, 20);
        sem_init(&mutex, 0, 1);
     
    	while ((nbr_prod+nbr_cons)<20){ //max 20 prod/cons
     
    	    tirage=rand()%3;//nombre aleatoire entre 0 et 3
     
    		if (tirage >1) //si la valeur du tirage 2 ou 3 le main lance un cons
    		pthread_create(&conso[nbr_cons++], NULL, consommation, (void *)&liste_produits);
    		else //si la valeur du tirage 0 ou 1 le main lance un prod
    		pthread_create(&prod[nbr_prod++], NULL, production, (void *)&liste_produits);
     
     
    	}
     
    for (i=0;i<=nbr_prod;i++) pthread_join(prod[i],NULL);
    for (i=0;i<=nbr_cons;i++) pthread_join(conso[i],NULL);
     
     
    	return 0;
    }
     
    /*******************************************************/
    void * production(void * arg)
    {
     
     
    		sem_wait(&vide);
                sem_wait(&mutex);
                    liste_produits[++indexe] = 'a';
                    printf("Production de l'element 'a', nbElement vaut %d\n", indexe);
                    printf ("La liste des produits est : %s\n\n",liste_produits);
                sem_post(&mutex);
    		sem_post(&pleine);
     
            pthread_exit(NULL);
        	return NULL;
    }
     
    /*********************************************************/
    void * consommation(void * arg)
    {
     
    		sem_wait(&pleine);
                sem_wait(&mutex);
                    liste_produits[indexe--] = '\0';
                    printf("Consommation de l'element 'a', nbElement vaut %d\n", indexe);
                    printf ("La liste des produits est : %s\n\n",liste_produits);
                sem_post(&mutex);
    		sem_post(&vide);
     
            pthread_exit(NULL);
    		return NULL;
    }

+ 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