Threads Posix et verrous

Threads Posix et verrous - C - Programmation

Marsh Posté le 21-12-2006 à 21:32:37    

Bonjour  :hello:  
 
Je cherche à savoir utiliser les verrous qui fonctionnent avec les pthreads.
Ainsi, j'ai fait un petit programme qui permet à deux pthreads de pouvoir incrémenter successivement puis afficher une variable.
Seulement voilà, le programme n'affiche rien. En fait, je pense que le programme ne cree par les pthread correctement.
Merci d'avance de pouvoir m'aider.
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. typedef struct{
  5. pthread_mutex_t * verrou;
  6. char * message;
  7. int * ressource;
  8. } vermess;
  9. void * routine (vermess * V)
  10. {
  11. int i;
  12. for(i=0;i<10;i++)
  13. {
  14.  pthread_mutex_lock (V->verrou);
  15.  V->ressource++;
  16.  printf("%s %d\n",V->message,V->ressource);
  17.  pthread_mutex_unlock (V->verrou);
  18. }
  19.     pthread_exit (0);
  20. }
  21. int main()
  22. {
  23. int erreur1,erreur2, argument = 3;
  24. int ressource=0;
  25.     pthread_t p1,p2;
  26. pthread_mutex_t verrou;
  27. pthread_mutex_init(&verrou, NULL);
  28. vermess * V1=(vermess *)malloc(sizeof(vermess));
  29. V1->verrou=&verrou;
  30. V1->ressource=&ressource;
  31. V1->message="Je suis le thread 1 : ";
  32. vermess * V2=(vermess *)malloc(sizeof(vermess));
  33. V2->verrou=&verrou;
  34. V2->ressource=&ressource;
  35. V2->message="Je suis le thread 2 :     ";
  36.     erreur1 = pthread_create (&p1, NULL, routine, V1);
  37. erreur2 = pthread_create (&p2, NULL, routine, V2);
  38.     if (erreur1 != 0) fprintf(stderr,"Echec creation de thread: %d\n",erreur1);
  39.  if (erreur2 != 0) fprintf(stderr,"Echec creation de thread: %d\n",erreur2);
  40. return 0;
  41. }


 
La structure vermess (pour Verrou-Message) permet de faire passer deux arguments dans la fonction routine.

Reply

Marsh Posté le 21-12-2006 à 21:32:37   

Reply

Marsh Posté le 21-12-2006 à 22:00:30    

Bien que je ne connaisse pas les pthreads (donc important disclaimer), ton code a l'air pas mal. Par contre, la doc de pthread_mutex_lock me dit que l'aquisition du lock n'est pas réentrante (ce qui n'est pas étonnant de la part de Xavier Leroy), donc le risque de deadlock est important.
Que se passe-t'il si tu remplaces l'appel par pthread_mutex_trylock, ou que tu changes le type du mutex en PTHREAD_MUTEX_RECURSIVE ou PTHREAD_MUTEX_ERRORCHECK ?
De façon générale, pour un usage standard (où les perfs maximales ne sont pas absolument cruciales), je privilégierais plutôt le trylock sur le lock de base.

Message cité 1 fois
Message édité par el muchacho le 21-12-2006 à 22:02:43
Reply

Marsh Posté le 21-12-2006 à 22:01:20    

Déja, par principe, met des pthread_join, des fois que..


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:03:13    

el muchacho a écrit :

Bien que je ne connaisse pas les pthreads (donc important disclaimer), ton code a l'air pas mal. Par contre, la doc de pthread_mutex_lock me dit que l'aquisition du lock n'est pas réentrante (ce qui n'est pas étonnant de la part de Xavier Leroy), donc le risque de deadlock est important.  
?


Euh, si les mutex sont pas réentrants, on risque pas d'être mal barrés pour les utiliser ?


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:04:21    

farib a écrit :

Euh, si les mutex sont pas réentrants, on risque pas d'être mal barrés pour les utiliser ?


Ben si:
"If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection is not provided. Attempting to relock the mutex causes deadlock. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, undefined behaviour results.
(...)
If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to recursively lock the mutex results in undefined behaviour. Attempting to unlock the mutex if it was not locked by the calling thread results in undefined behaviour. Attempting to unlock the mutex if it is not locked results in undefined behaviour."

 

C'est chaud à utiliser.


Message édité par el muchacho le 21-12-2006 à 22:07:25
Reply

Marsh Posté le 21-12-2006 à 22:12:20    

bah ça c si tu fais deux locks comme un con [:spamafote]
si t'enchaines  

Code :
  1. pthread_lock(mutex1);
  2. pthread_mutex_lock(mutex2);


forcément t mal barré !
 
en l'occurrence son pb se règle en mettant des pthread_join
 
en fait, sans, le thread principal meurt et emmene avec lui son stdout, un strace donne:
 

sendto(-1210598640, umovestr: Input/output error


 
y'a un 2e bug, c V->ressour++
 
tu incémentes un pointeur, erreur


Message édité par farib le 21-12-2006 à 22:14:32

---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:13:57    

Merci pour vos réponses !
En fait j'ai rajouté les pthread_join() et du coup ca marche.
Seulement ca ne fonctionne pas comme je le veux :  
 
Je suis le thread 1 :  4
Je suis le thread 1 :  8
Je suis le thread 1 :  12
Je suis le thread 1 :  16
Je suis le thread 1 :  20
Je suis le thread 1 :  24
Je suis le thread 1 :  28
Je suis le thread 1 :  32
Je suis le thread 1 :  36
Je suis le thread 1 :  40
Je suis le thread 2 :      4
Je suis le thread 2 :      8
Je suis le thread 2 :      12
Je suis le thread 2 :      16
Je suis le thread 2 :      20
Je suis le thread 2 :      24
Je suis le thread 2 :      28
Je suis le thread 2 :      32
Je suis le thread 2 :      36
Je suis le thread 2 :      40
 
Voilà ce qu'il m'affiche.
Là il est clair que les verrous n'ont pas l'air de fonctionner.
Seulement voilà si vous pouviez encore m'aider pour resoudre ce problème car je ne vois pas la solution...
 
edit - en effet, j'ai modifié entretemps le probleme pour l'acces a la ressource


Message édité par HomerJK le 21-12-2006 à 22:15:19
Reply

Marsh Posté le 21-12-2006 à 22:14:57    

pointeur cf remarque du dessus

 

edit spassa
edit2 touvabien


Message édité par farib le 21-12-2006 à 22:19:23

---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:20:20    

farib> Pas forcément: ici, je ne vois pas ce qui empêche de tomber dans le cas où p1 locke le verrou et p2 tente de le délocker.

Message cité 1 fois
Message édité par el muchacho le 21-12-2006 à 22:21:34
Reply

Marsh Posté le 21-12-2006 à 22:21:36    

el muchacho a écrit :

Pas forcément: ici, je ne vois pas ce qui empêche de tomber dans le cas où p1 locke le verrou et p2 tente de le délocker.


 
oui oui,  
 
le deadlock est avant tout une erreur de conception du programme et d'utilisation des threads, alors c réglé :o


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:21:36   

Reply

Marsh Posté le 21-12-2006 à 22:24:33    

Et je pense que c'est ce qui se passe. [:spamafote]
Il serait intéressant de voir ce que ça donne avec trylock.
Et pthread_join, finalement, ça sérialise les 2 threads  (ce qu'on voit dans sa sortie), si je comprends bien, puisqu'on attend la mort de p1 ?

Message cité 1 fois
Message édité par el muchacho le 21-12-2006 à 22:25:46
Reply

Marsh Posté le 21-12-2006 à 22:28:12    

Quand je fais V->ressource++; j'incrémente le pointeur mais pas la valeur du pointeur ? Une solution ?

Reply

Marsh Posté le 21-12-2006 à 22:28:19    

el muchacho a écrit :

Et je pense que c'est ce qui se passe. [:spamafote]
Il serait intéressant de voir ce que ça donne avec trylock.
Et pthread_join, finalement, ça sérialise les 2 threads  (ce qu'on voit dans sa sortie), si je comprends bien, puisqu'on attend la mort de p1 ?


 
nan  
 
pthread_join empeche juste le thread main() de crever avant que les threads ne s'exécutent
 
apres au niveau des mutexes, il n'y a pas de garanties quand au réveil des threads, au niveau du scheduler, etc...
 
dans le cas présent, comme c'est simple, il a le temps de faire tout le premier thread avant que le scheduler linux (ou autre) bascule vers l'autre thread

Message cité 1 fois
Message édité par farib le 21-12-2006 à 22:30:01

---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:29:39    

HomerJK a écrit :

Quand je fais V->ressource++; j'incrémente le pointeur mais pas la valeur du pointeur ? Une solution ?


(*(V->ressource))++;
 
 
je blinde de parentheses parce que je suis aps sur des priorités des opérateurs


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:30:56    

J'ai essayé avec trylock mais ca n'a pas changé le résultat d'affichage :

Code :
  1. void * routine (vermess * V)
  2. {
  3. int i, test;
  4. for(i=0;i<10;i++)
  5. {
  6.  do
  7.  {
  8.  test=pthread_mutex_trylock (V->verrou);
  9.  }while(test!=0);
  10.  V->ressource++;
  11.  printf("%s %d\n",V->message,V->ressource);
  12.  pthread_mutex_unlock (V->verrou);
  13. }
  14.     pthread_exit (0);
  15. }

Reply

Marsh Posté le 21-12-2006 à 22:31:19    

farib a écrit :


dans le cas présent, comme c'est simple, il a le temps de faire tout le premier thread avant que le scheduler linux (ou autre) bascule vers l'autre thread


Ah ok, c'est bien possible, en effet (malgré les printf).

Reply

Marsh Posté le 21-12-2006 à 22:31:59    

t'as modifié quoi ?
 
avec les join, les modifs sur ressource (le pointeur comme je l'ai donnée, et aussi le printf)


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:32:23    

HomerJK a écrit :

J'ai essayé avec trylock mais ca n'a pas changé le résultat d'affichage :

Code :
  1. void * routine (vermess * V)
  2. {
  3. int i, test;
  4. for(i=0;i<10;i++)
  5. {
  6.  do
  7.  {
  8.  test=pthread_mutex_trylock (V->verrou);
  9.  }while(test!=0);
  10.  V->ressource++;
  11.  printf("%s %d\n",V->message,V->ressource);
  12.  pthread_mutex_unlock (V->verrou);
  13. }
  14.     pthread_exit (0);
  15. }



Même en enlevant le pthread_join et en augmentant le nbre d'itérations de la boucle à 10000?
 
edit: lis la doc: il faut décrémenter autant de fois le lock count qu'il a été incrémenté, sinon le lock n'est pas libéré.


Message édité par el muchacho le 21-12-2006 à 22:37:39
Reply

Marsh Posté le 21-12-2006 à 22:32:47    

el muchacho a écrit :

Ah ok, c'est bien possible, en effet (malgré les printf).


c'est juste sûr [:spamafote]


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:35:44    

Faudrait rajouter des usleep() a base de random pour forcer le mutex à travailler


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:39:15    

arf, je m'y retrouve plus :)
en fait (*(V->ressource))++;  ne fonctionne pas il me met une erreur de segmentation.
Sinon je ne peux pas enlever les pthread_join sinon ca ne marche pas.
Je vais essayer vos autres méthodes.
Merci encore pour les réponses.

Reply

Marsh Posté le 21-12-2006 à 22:41:10    

chezmoi ça marche [:spamafote]


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:41:47    

HomerJK a écrit :

arf, je m'y retrouve plus :)
en fait (*(V->ressource))++;  ne fonctionne pas il me met une erreur de segmentation.
Sinon je ne peux pas enlever les pthread_join sinon ca ne marche pas.
Je vais essayer vos autres méthodes.
Merci encore pour les réponses.


 
t'as modifié quoi concernant les pointeurs ?


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 22:45:03    

En fait pas grand-chose, je vais reassayer tout ca demain, je dois vraiment m'en aller désolé...
Je vous remercie encore pour votre aide.
@demain

Reply

Marsh Posté le 21-12-2006 à 22:49:40    

Euh, j'ai pas de compilo sous la main pour tester mais à vue de nez j'aurais dit que V->ressource++, c'est correct et par contre l'initialisation, c'est  V->ressource= ressource (et non &ressource).
Mais ta correction farib devrait marcher aussi.

Message cité 1 fois
Message édité par el muchacho le 21-12-2006 à 22:52:06
Reply

Marsh Posté le 21-12-2006 à 23:03:28    

el muchacho a écrit :

Euh, j'ai pas de compilo sous la main pour tester mais à vue de nez j'aurais dit que V->ressource++, c'est correct et par contre l'initialisation, c'est  V->ressource= ressource (et non &ressource).
Mais ta correction farib devrait marcher aussi.


 
V-> ressource est un int *


---------------
Bitcoin, Magical Thinking, and Political Ideology
Reply

Marsh Posté le 21-12-2006 à 23:08:54    

ah mince, c't'erreur de nOOb... [:arn0]


Message édité par el muchacho le 21-12-2006 à 23:10:03
Reply

Marsh Posté le 21-12-2006 à 23:30:40    

HomerJK a écrit :

Je cherche à savoir utiliser les verrous qui fonctionnent avec les pthreads.
Ainsi, j'ai fait un petit programme qui permet à deux pthreads de pouvoir incrémenter successivement puis afficher une variable.


http://mapage.noos.fr/emdel/pthreads.htm


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 22-12-2006 à 19:30:04    

Re-bonjour
Pour le probleme d'incrementation j'ai reglé mon probleme, je suis passé par une valeur intermédiaire. :  

Code :
  1. int temp=(int) V->ressource;
  2. temp++;
  3. V->ressource=(int *) temp;


Sinon sur la page dont emmanuel Delahaye m'a filé le lien, j'ai fait un copier/coller d'un programme de synchronisation de verrous qui fonctionnait sans probleme, puis je l'ai exécuté sur le mien : le resultat est exactement le meme que celui de mon programme.
Je ne comprends pas...
Sinon j'ai rajouté un sleep(1) sur mon programme :

Code :
  1. void * routine (vermess * V)
  2. {
  3. int i, test;
  4. for(i=0;i<10;i++)
  5. {
  6.  do
  7.  {
  8.  test=pthread_mutex_trylock (V->verrou);
  9.  }while(test!=0);
  10.  int temp=(int) V->ressource;
  11.  temp++;
  12.  V->ressource=(int *) temp;
  13.  printf("%s %d\n",V->message,V->ressource);
  14.  do
  15.  {
  16.  test=pthread_mutex_unlock (V->verrou);
  17.  }while(test!=0);
  18.  sleep(1);
  19. }
  20.     pthread_exit (0);
  21. }


 Et voici l'affichage :  
Je suis le thread 1 :  1
Je suis le thread 2 :      1
Je suis le thread 1 :  2
Je suis le thread 2 :      2
Je suis le thread 1 :  3
Je suis le thread 2 :      3
Je suis le thread 1 :  4
Je suis le thread 2 :      4
Je suis le thread 1 :  5
Je suis le thread 2 :      5
Je suis le thread 1 :  6
Je suis le thread 2 :      6
Je suis le thread 1 :  7
Je suis le thread 2 :      7
Je suis le thread 1 :  8
Je suis le thread 2 :      8
Je suis le thread 1 :  9
Je suis le thread 2 :      9
Je suis le thread 1 :  10
Je suis le thread 2 :      10
 
Lors de l'affichage, le sleep(1) ne prend effet que tous les deux messages (apres "je suis le thread 2" ), de même que l'incrémentation de la variable ressource.
Or, au bout du compte, ressource devrait atteindre la valeur 20, et non 10 comme ici.

Message cité 1 fois
Message édité par HomerJK le 22-12-2006 à 19:30:58
Reply

Marsh Posté le 22-12-2006 à 19:53:25    

HomerJK a écrit :

Re-bonjour
Pour le probleme d'incrementation j'ai reglé mon probleme, je suis passé par une valeur intermédiaire. :

Code :
  1. int temp=(int) V->ressource;
  2. temp++;
  3. V->ressource=(int *) temp;




Tu cherches à incrémenter quoi ? Le pointeur ou la valeur pointée ?

 

Si c'est le pointeur :

Code :
  1. V->ressource++;


Si c'est la valeur pointée :

Code :
  1. (*V->ressource)++;



Message édité par Emmanuel Delahaye le 22-12-2006 à 20:45:24

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 22-12-2006 à 20:03:40    

Je cherchais à incrémenter la valeur, mais j'ai réglé mon probleme.
Sinon ta méthode d'incrémentation de la valeur pointée (*V->ressource)++; provoque une erreur de segmentation.

Reply

Marsh Posté le 22-12-2006 à 20:51:26    

HomerJK a écrit :

Je cherchais à incrémenter la valeur, mais j'ai réglé mon probleme.
Sinon ta méthode d'incrémentation de la valeur pointée (*V->ressource)++; provoque une erreur de segmentation.


Curieux. Tu es sûr que le pointeur est correct ?
 

Code :
  1. #include <stdio.h>
  2. struct data
  3. {
  4.    int *pi;
  5. };
  6. static void prt_i (struct data const *p_data)
  7. {
  8.    printf ("i=%d\n", *p_data->pi);
  9. }
  10. static void inc_i (struct data *p_data)
  11. {
  12.    (*p_data->pi)++;
  13. }
  14. int main (void)
  15. {
  16.    int i = 0;
  17.    struct data data = { &i };
  18.    prt_i (&data);
  19.    inc_i (&data);
  20.    prt_i (&data);
  21.    return 0;
  22. }



i=0
i=1
 
Press ENTER to continue.


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 22-12-2006 à 21:10:45    

C'est bon j'ai réglé mon probleme.
En fait c'était une mauvaise manipulation du pointeur : chaque thread accédait à leur propre variable ressource.
Maintenant, c'est bon, ils ont en commun une seule variable ressource.
Du coup, l'instruction (*V->ressource)++; marche.
 
Voila ce que ca m'affiche maintenant :  
Je suis le thread 1 :  1
Je suis le thread 2 :      2
Je suis le thread 1 :  3
Je suis le thread 2 :      4
Je suis le thread 1 :  5
Je suis le thread 2 :      6
Je suis le thread 1 :  7
Je suis le thread 2 :      8
Je suis le thread 1 :  9
Je suis le thread 2 :      10
Je suis le thread 1 :  11
Je suis le thread 2 :      12
Je suis le thread 1 :  13
Je suis le thread 2 :      14
Je suis le thread 1 :  15
Je suis le thread 2 :      16
Je suis le thread 1 :  17
Je suis le thread 2 :      18
Je suis le thread 1 :  19
Je suis le thread 2 :      20
 
Seulement cela fonctionne qu'avec l'intruction sleep(1); Il est possible de faire en sorte que cela fonctionne sans ?

Reply

Marsh Posté le 22-12-2006 à 21:31:16    

HomerJK a écrit :

Seulement cela fonctionne qu'avec l'intruction sleep(1); Il est possible de faire en sorte que cela fonctionne sans ?


Si il y a un mécanisme de synchronisation, oui, ce sera selon le bon vouloir du système. De toutes façons, une tache que ne rend pas la main, c'est crade. Un système un peu malin pourrait très bien la punir en augmentant son ratio de temps, voir en la supprimant carrément...
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 23-12-2006 à 14:41:36    

Comment ça ne fonctionne qu'avec sleep ?

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed