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

GTK+ avec C & C++ Discussion :

Problème de rafraichissement [EDIT: Synchronisation entre threads ]


Sujet :

GTK+ avec C & C++

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 36
    Par défaut Problème de rafraichissement [EDIT: Synchronisation entre threads ]
    Bonjour,

    Je crée un autre post puisque cela n'a plus grand chose à voir avec mon interrogation de la veille

    Je vous explique rapidement ma situation:
    Je suis en train de créer un programme multi-thread (librairie pthread) comportant d'un côté ma logique métier, de l'autre mon interface (le cas échéant avec GTK).

    Pourquoi créer des threads ? Pour pouvoir exploiter les différents coeurs de mon processeur, mais aussi pour que l'utilisateur puisse tout le temps avoir la main sur l'application, en cas de gros calcul par exemple.

    Je communique entre mes threads via des signaux UNIX, pour assurer deux choses:
    - Ne pas devoir mettre un thread en attente d'un autre (ce qui aurait été le cas si je les avais synchronisés avec des sémaphores).
    - Assurer l'indépendance entre ma logique métier et son mon interface, ce qui me permettra par la suite de pouvoir changer totalement cette dernière sans devoir ne serait-ce qu'ouvrir le code de ma logique métier.

    Bref, ça c'était l'architecture de mon application.

    J'ai un problème en ce qui concerne la mise à jour de ma grille:

    Pour expliquer la situation, l'utilisateur clique sur un bouton, la vue envoie un signal de démarrage à la logique métier. Cette dernière envoie toutes les X secondes un message de mise à jour à ma vue, tant que la vue ne lui a pas envoyé de signal d'arrêt.

    Ma grille est déclarée dynamiquement, l'accès en lecture ou en écriture étant gérée par un sémaphore.

    Au bout d'un certain nombre de secondes, aléatoire, ma vue ne se met plus à jour et bloque sur l'instruction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     gtk_widget_queue_draw(widgets.plateau);
    J'ai déjà vu sur certains sites qu'une mauvaise utilisation des threads pouvait aboutir à ce type de comportement. Cependant je n'utilise pas du tout les threads GTK, mon interface étant dans un thread qui lui est dédié, et je n'en crée pas d'autres. J'ai tenté de débugger pendant plusieurs heures mon programme pour trouver le problème, en vain.


    J'ai remarqué que j'avais parfois le même problème lorsque j'initialise ma grille (dans ce cas, il n'y a qu'un aller-retour entre modèle et vue question signaux), à savoir lorsque j'exécute le code suivant:

    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
      /* Destruction du label */
      if (widgets.lblInit != NULL) {
        gtk_widget_destroy(widgets.lblInit);
        widgets.lblInit = NULL;
      }
      /* Si le plateau a déjà été initialisé, on le détruit */
      if (widgets.plateau != NULL) {
        gtk_widget_destroy(widgets.plateau);
        widgets.plateau = NULL;
      }
     
      /* On crée la grille du jeu */
      widgets.plateau = gtk_drawing_area_new();
     
      /* Ajout de la grille dans la box verticale */
      gtk_box_pack_start(GTK_BOX(widgets.box), widgets.plateau, TRUE, TRUE, 0);
     
      /* A l'affichage, la fonction de dessin doit être appelée */    
      g_signal_connect(G_OBJECT(widgets.plateau), "expose_event", G_CALLBACK(affichage_grille), NULL);
      gtk_widget_show(widgets.plateau);
    De plus, très rarement j'ai ce message:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (<unknown>:3189): Gtk-CRITICAL **: IA__gtk_widget_get_visible: assertion `GTK_IS_WIDGET (widget)' failed
    Il n'est pas bloquant mais récurrent, et je n'en trouve pas l'origine.

    Merci pour votre lecture, j'espère que vous pourrez m'aider En attendant, j'essaye de fouiller un peu partout (debug, doc,...).

  2. #2
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 36
    Par défaut
    Bonjour,

    Je suis en train de débugger mon code, visiblement j'ai un problème au niveau de mes destructions de widgets.

    En effet le message:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (<unknown>:3189): Gtk-CRITICAL **: IA__gtk_widget_get_visible: assertion `GTK_IS_WIDGET (widget)' failed
    Semble venir du code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
      /* Destruction du label */
      if (widgets.lblInit != NULL) {
        gtk_widget_destroy(widgets.lblInit);
        widgets.lblInit = NULL;
      }
      /* Si le plateau a déjà été initialisé, on le détruit */
      if (widgets.plateau != NULL) {
        gtk_widget_destroy(widgets.plateau);
        widgets.plateau = NULL;
      }
    widgets est une variable globale statique (structure contenant mes différents éléments graphiques). Je vais vérifier mon code, peut être que l'on rentre dans un if alors que le widget concerné est déjà détruit

    EDIT: En fait, il semblerait que l'erreur affichée provienne du plateau.

    Elle n'apparaît jamais si ces lignes sont commentées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
      /* On crée la grille du jeu */
        widgets.plateau = gtk_drawing_area_new();
     
      /* Ajout de la grille dans la box verticale */
      gtk_box_pack_start(GTK_BOX(widgets.box), widgets.plateau, TRUE, TRUE, 0);

  3. #3
    Membre confirmé Avatar de Gamall
    Profil pro
    Étudiant ENSEA
    Inscrit en
    Août 2009
    Messages
    252
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant ENSEA

    Informations forums :
    Inscription : Août 2009
    Messages : 252
    Par défaut
    Rajoute juste avant le gtk_widget_show un truc du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g_assert (GTK_IS_WIDGET(widgets.plateau));
    Pour voir si ton widget passé en paramètre est vraiment un GtkWidget. Par contre, faudra peut-être compiler en debug.

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 36
    Par défaut
    Salut,

    Merci pour ta réponse, malheureusement j'ai eu le même message sans avoir d'indication supplémentaire.

    Je vais regarder pour le mode debug, peut être utiliser gdb. J'ai l'impression d'avoir pas mal d'erreurs dans mon code malgré de nombreuses corrections, ça doit être le multi-thread, c'est bizarre.

    EDIT: Du nouveau !
    Visiblement je faisais fausse piste, il semblerait que j'ai un problème avec mes signaux puisque gdb me signale que lorsque mon thread plante,
    Program received signal SIGSYS, Bad system call.
    Mes envois de signaux fonctionnent de la manière suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /* Envoie un message au modèle par l'intermédiaire du signal SIGNAL_ACTION_MODELE */
    void envoi_signal_modele(char* message) {
      struct sembuf operation;
      operation.sem_num = 0;
      operation.sem_op = -1;
      operation.sem_flg = 0;
      /* Demande et (attente éventuelle) d'une autorisation d'envoi */
      semop(varCommunes->semSignalModele, &operation, 1);
      varCommunes->message = message; /* Ecriture du message en variable commune aux threads*/
      /* Envoi du signal */ 
      pthread_kill(*(varCommunes->modele), SIGNAL_ACTION_MODELE);
      printf("message envoyé \n");
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    void envoyer_message_vue(char* message) {
      struct sembuf operation;
     
      operation.sem_num = 0;
      operation.sem_op = -1;
      operation.sem_flg = 0;
     
      semop(varCommunes->semSignalVue, &operation, 1);
      varCommunes->message = message;
     
      pthread_kill(*(varCommunes->vue), SIGNAL_MAJ_VUE);
      printf("message envoyé à la vue \n");
    }
    Sachant que les signaux sont définis dans un en-tête:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #ifndef __CONSTANTES__
    #define __CONSTANTES__
    /* Constantes globales au programme */
    /* Ne doivent figurer ici que les constantes indépendantes de la vue ou du modèle */
     
    #define SIGNAL_MAJ_VUE SIGUNUSED
    #define SIGNAL_ACTION_MODELE SIGCHLD
     
    /* Messages vers le modèle */
    [...]
    /* Messages vers la vue */
    [...]
     
    #endif
    Et que les threads sont définis et initialisés dans un objet de type structure:

    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
    #ifndef __STRUCT_ARGS__
    #define __STRUCT_ARGS__
     
    typedef struct {
     
      /* Identifiants des threads */
      pthread_t* vue;
      pthread_t* modele;
     
      /* Sémaphores à utiliser avant tout envoi de signal */
      /* Permet de garantir que tout signal envoyé est traité.
       * Un signal peut être envoyé lorsqu'il a été dérouté ou réarmé. 
       */
      int semSignalVue; /* Vers la vue */
      int semSignalModele; /* Vers le modèle */
     
      /* Message transmis: valeurs dans le fichier constantes.h */
      /* Ecrire dans cette variable uniquement lorsque l'on est en section critique:
       * Ecrire le message avant l'envoi du signal, une fois le jeton du sémaphore obtenu*/
      char* message;
     
      /* Variables du jeu */
      [...]
    } args;
     
    #endif
    Note: dans chaque thread, varCommunes est déclaré comme cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static args* varCommunes = NULL;
    Initialisé dans le main, adresse transmise à la création des threads:

    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
     
      /* Déclarations communes aux threads */
      args* argumentsThreads = (args*)malloc(sizeof(args));
     
      /* Initialisation des arguments */
      argumentsThreads->vue = (pthread_t *)malloc(sizeof(pthread_t));
      argumentsThreads->modele = (pthread_t *)malloc(sizeof(pthread_t));
      [...]
     
      creation_semaphores(argumentsThreads, argv[0]);
     
      /* Création des threads. */
      pthread_create(argumentsThreads->vue, NULL, main_vue, argumentsThreads); /* La vue doit connaître le modèle */
      pthread_create(argumentsThreads->modele, NULL, main_modele, argumentsThreads); /* Le modèle doit connaître la vue*/
     
      /* Attente de la fin des threads */
      pthread_join(*(argumentsThreads->vue), NULL);
      pthread_join(*(argumentsThreads->modele), NULL);
     
    /* Libération de la mémoire allouée */
      free(argumentsThreads->vue);
      free(argumentsThreads->modele);
      free(argumentsThreads);
    A la base, je souhaitais utiliser SIGUSR1 et SIGUSR2 qui semblent destinés aux applications mais cela ne marchait visiblement pas, il faut que je regarde comment faire

    Voilà, je vous donne tout ça au cas ou quelque chose vous saute aux yeux, merci

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 36
    Par défaut
    Je confirme que le problème vient de mes signaux, je pense que je les utilise mal et à mon avis, l'envoi de signaux entre threads pose problème. Peut être que quand j'envoie un signal à un thread, parfois c'est l'autre qui le reçoit, ou quelque chose comme cela. Je vais regarder ça !

    Edit: C'est vraisemblablement le problème: il arrive que le signal de mise à jour ne soit pas intercepté, même en utilisant des sémaphores pour gérer les envois / réceptions de signaux. Il ne me reste qu'à trouver une solution pour synchroniser mes threads, avec ou sans signaux mais surtout sans état bloquant.

    Re-Edit: J'ai décidé de synchroniser mes threads avec des états bloquants, pour une petite appli de ce type cela ne sera pas gênant. Finalement, je pense même que bien gérée, la synchro par sémaphore peut être invisible pour l'utilisateur en cas de gros traitement.

Discussions similaires

  1. Synchronisation entre Threads
    Par hotman1313 dans le forum Concurrence et multi-thread
    Réponses: 7
    Dernier message: 24/02/2015, 17h38
  2. Synchronisation entre Thread
    Par Red Sno dans le forum Débuter
    Réponses: 2
    Dernier message: 12/12/2012, 21h38
  3. Problème de synchronisation entre Thread et VCL
    Par Jipété dans le forum Débuter
    Réponses: 33
    Dernier message: 21/05/2012, 11h14
  4. Réponses: 2
    Dernier message: 29/04/2007, 19h59
  5. Synchronisation entre 2 threads
    Par bodbod dans le forum C++
    Réponses: 8
    Dernier message: 20/08/2004, 18h29

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