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 :

Problème avec les synchronisations de threads


Sujet :

C

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 9
    Points : 9
    Points
    9
    Par défaut Problème avec les synchronisations de threads
    Bonjour!

    Je suis (toujours) en train de coder la simulation d'un bus CAN en C, et j'en suis à la phase de débuggage. Tous les problèmes que j'ai pu rencontrer ont été résolus, sauf un que je ne comprend vraiment pas.

    J'utilise plusieurs threads pour ce projet, et en particulier les deux threads suivants :

    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
     
     
    		while(1)
    		{
    			usleep(905);  // sleep 0.905 ms
    			time_since_beginning=time_since_beginning+1; // time
     
                            printf("  TIC  ");
     
    			if(time_since_beginning%10==0)
    			{
    				pthread_mutex_lock (&mutex3); 
    				pthread_cond_broadcast (&t_to_0ms);
    				pthread_mutex_unlock (&mutex3);
    			}
    			else if(time_since_beginning%10==3)
    			{
    				pthread_mutex_lock (&mutex4); 
    				pthread_cond_broadcast (&t_to_3ms);
    				pthread_mutex_unlock (&mutex4);
    			}
    			else if(time_since_beginning%10==5)
    			{
    				pthread_mutex_lock (&mutex5); 
    				pthread_cond_broadcast (&t_to_5ms);
    				pthread_mutex_unlock (&mutex5);
    			}
    			else if(time_since_beginning%10==6)
    			{
    				pthread_mutex_lock (&mutex6);  
    				pthread_cond_broadcast (&t_to_6ms);
    				pthread_mutex_unlock (&mutex6);
    			}
    			else if(time_since_beginning%10==7)
    			{
    				pthread_mutex_lock (&mutex7); 
    				pthread_cond_broadcast (&t_to_7ms);
    				pthread_mutex_unlock (&mutex7);
    			}
    			else if(time_since_beginning%10==9)
    			{
    				pthread_mutex_lock (&mutex8); 
    				pthread_cond_broadcast (&t_to_9ms);
    				pthread_mutex_unlock (&mutex8);
    			}
     
    		}
    	}
        }
    Ce thread me sert d'horloge, et est utilisé pour synchroniser tous les autres threads entre eux. Il écrit TIC dans la console tous les 10 ms.





    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
    		while(1)    
    		{
    			counter1++;
     
    printf("a");
     
    			pthread_mutex_lock(&mutex3); 
    			pthread_cond_wait (&t_to_0ms, &mutex3); 
    			pthread_mutex_unlock(&mutex3);
    printf("b");			
     
     
    //instructions rapides, du genre if(something), printf(something)
     
     
    printf("e");
    			//pthread_mutex_lock(&mutex4); 
    			//pthread_cond_wait (&t_to_3ms, &mutex4); // on attend qu'un calcul soit fait dans un autre thread
    			//pthread_mutex_unlock(&mutex4);
    printf("f"); 
    			pthread_mutex_lock(&mutex5); 
    			pthread_cond_wait (&t_to_5ms, &mutex5); // le calcul est fini dans l'autre thread
    			pthread_mutex_unlock(&mutex5);
    printf("g");
     
    //instructions rapides, du genre if(something), printf(something)
     
    printf("h");		
    		}
    Ce morceau de thread est ce qui fait bugger mon programme, de manière totalement aléatoire. Par exemple, le programme peut tourner pendant longtemps avant de bugger (genre 10 minutes, avec un tour de boucle fait toutes les 10 ms), ou le faire dans les 1ères secondes.

    Tous les threads sont synchronisés entre eux de façon à faire des boucles qui se répètent toutes les 10 ms. Le problème est que ce thread en particulier se désynchronise parfois des autres, ce qui provoque une erreur dans mes calculs et fait s'arrêter mon programme.
    Il y a des problèmes qui apparaissent à deux endroits : au niveau du printf("f") et du printf("a").
    Je voudrais que le programme avance 'logiquement', et que toutes les 10 ms j'affiche

    ... TIC abcdefgh TIC abcdefgh TIC ...
    C'est effectivement ce qui se passe presque tout le temps, mais parfois à la place j'ai

    ... TIC abcdefgh TIC abcdef TIC gh TIC abcdefgh TIC ...
    Le thread semble 'louper' le broadcast de t_to_5ms, et ne le récupère qu'au tour de boucle suivant. Il se passe parfois la même chose au niveau de printf("a"), ou le thread reste bloqué pour un tour de boucle en attente du t_to_0ms.


    Le plus étrange c'est que l'erreur n'est pas systèmatique et peut arriver à n'importe quel moment, au premier tour de boucle comme au 10 000 ème. Mes autres threads utilisent les mêmes pthread_cond_wait et marchent correctement.



    Si vous avez une idée sur pourquoi cela fait ça, ou encore mieux, une solution au problème, je serais très heureux!

  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
    Je pense qu'en écrivant ton code t'as du te douter qu'écrire mutex1...n c'était pas une bonne solution... Si tu veux synchroniser tes threads pour qu'ils fassent tous un traitement au même moment chaque X seconde, il te suffit d'utiliser une barrière (http://fr.wikipedia.org/wiki/Barri%C...ynchronisation). Tu fais une petite abstraction et ça ne nécessitera que 1 mutex. Tu pourras toujours avoir un thread qui attend 1 seconde avant de passer la barrière mais note que cette borne est minimal, si un autre thread met plus que 1 seconde pour faire le traitement alors ils attendront tous qu'il ait fini.

    Pour ton code (que tu dois absolument changer de toutes manières) il me semble que ton problème vient du fait que parfois un de tes threads arrivent après le broadcast sur le wait et donc doit attendre le prochain broadcast (ceci parce qu'il met plus de 1 seconde à s'exécuter).

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 9
    Points : 9
    Points
    9
    Par défaut
    Tout d'abord, merci de ta réponse. Je vais regarder de plus près ces barrières, mais leur utilisation me semble compliquée parce que je n'ai pas forcément toujours un nombre fixe de threads devant attendre x ms.

    Par contre, je ne comprend pas pourquoi les mutex ne marcheraient pas, vu que les problèmes se produisent à des endroits ou les threads sont forcément arrivés sur le wait.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    		pthread_mutex_lock(&mutex4); 
    			pthread_cond_wait (&t_to_3ms, &mutex4); // on attend qu'un calcul soit fait dans un autre thread
    			pthread_mutex_unlock(&mutex4);
    printf("f"); 
    			pthread_mutex_lock(&mutex5); 
    			pthread_cond_wait (&t_to_5ms, &mutex5); // le calcul est fini dans l'autre thread
    			pthread_mutex_unlock(&mutex5);
    Par exemple si je fais ça, le thread sera parfois bloqué au niveau de printf("f") pour un tour de boucle, alors qu'un printf ne prend certainement pas 2 ms à se faire. Le problème existe d'ailleurs même si j'augmente le temps entre deux 'tic' d'horloge.

  4. #4
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Je rejoins Trademark : utilise des barrières, c'est fait pour ça. Regarde pthread_barrier_X (init, wait, destroy).
    Mais un code qui ne fonctionne pas comme on veut est frustrant, je te comprends

    Pour revenir à ton code, tu ne peux pas être certain d'attendre sur la condition. Tu as conçu ton code à l'envers : au lieu de faire une barrière pour synchroniser tous les threads, tu as choisis de les faire attendre sur une condition tandis qu'un autre thread libère la condition de temps en temps. Il n'y a aucun moyen de savoir si tous les threads étaient en attente sur cette condition.
    Tu pourrais éventuellement faire des trucs très compliqués en utilisant des compteurs et des mutex supplémentaires pour savoir si tous tes threads sont en attente sur la condition. Mais cela revient à réinventer pthread_barrier_wait.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  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
    Juste pour relativiser, une barrière est en fait très simple, tu as un mutex, un compteur et une condition. Aucun thread ne passe la barrière tant que tout le monde n'est pas là.

    À chaque fois qu'un thread rentre dans la barrière tu incrémentes le compteur et le dernier thread (en testant si le compteur == nombre de thread) qui entre fait un broadcast (vu que cette zone de code est protégée par le mutex, on est sur que ce thread est bien le dernier). Pas si compliqué, si ?

    Mais comme l'a dit dinobogan, même si c'est bien de comprendre le principe et de le refaire une fois soit-même, rien ne vaut ce qui est déjà implémenté ;-)

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 9
    Points : 9
    Points
    9
    Par défaut
    J'ai enfin fini mon projet en utilisant les barrières, hourra!

    Pour pouvoir les utiliser j'ai dû changer totalement la logique de mon programme, ce qui m'a pris une bonne partie de l'après midi, mais son fonctionnement est effectivement beaucoup plus clair maintenant (et en plus, ça ne bug plus!). Merci encore pour vos réponses, elles m'ont vraiment énormément aidé!

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

Discussions similaires

  1. problème avec les commandes run() et start() de Thread
    Par -Juliette- dans le forum Concurrence et multi-thread
    Réponses: 8
    Dernier message: 18/06/2007, 10h08
  2. problèmes avec les threads
    Par lukbutor dans le forum C
    Réponses: 7
    Dernier message: 03/05/2007, 00h15
  3. Problèmes avec les thread et les pointeurs
    Par raspac dans le forum POSIX
    Réponses: 2
    Dernier message: 22/10/2006, 17h35
  4. [BOOST] Problème avec les threads
    Par SOAD08 dans le forum Dev-C++
    Réponses: 7
    Dernier message: 08/10/2006, 10h23
  5. Réponses: 5
    Dernier message: 10/05/2005, 10h22

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