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

Multithreading Discussion :

Synchronisation de threads


Sujet :

Multithreading

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 17
    Points
    17
    Par défaut Synchronisation de threads
    Bonjour, une partie de mon code réalise ceci :

    Un thread 1 réalise une lecture de données dans un fichier. Après avoir lu une certaine quantité de données il emet un signal, capté par le thread 2, puis se mets en attente avec une QWaitcondition.

    Le signal émit par le thread 1 réveille le thread 2 (QWaitCondition) qui traite les données. Lorsqu'il n'y a plus de données à traiter, le thread 2 émet un signal, capté par le thread 1, puis se met en attente (QwaitCondition).

    Le signal émit réveille le thread 1 qui lit la suite des données, etc..

    Problème :
    Pendant quelques tours de boucle, tout se passe bien puis le problème arrive. Entre le moment où le thread 2 émet son signal (afin de dire qu'il a terminé de traiter les données) et le moment ou il se met en attente, il se passe énormément de temps (alors que les lignes de codes sont consécutives).

    Pendant ce temps, le thread 1 a le temps de lire les données et de réveiller le thread 2 (qui ne s'est même pas encore endormi !). Du coup, tout le monde au dodo !

    Devrais-je procéder autrement ? Puis-je retarder le emit ou empêcher la scission entre le emit et la mise en attente ?

    Résumé
    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
    Thread 1 :
    
    debut boucle
       // lecture de données
    
       emit reveilThread2(&données)
    
       waitMutex1.lock();
       waitCondition1.wait(&waitMutex1)
       waitMutex1.unlock();
    fin boucle
    
    Thread 2 :
    
    début boucle
       si données
       // Traitement des données
       sinon
       {
       emit reveilThread1()   
       
       waitMutex2.lock();   
       waitCondition2.wait(&waitMutex2)
       waitMutex2.unlock();
       }
    fin boucle
    merci pour votre aide,
    Cédric.

  2. #2
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Bonjour Cédric-29

    Je ne vais pas pouvoir t'aider sur le problème de synchronisation des threads. D'autres sont plus compétent que moi.

    En voyant ton code, je me pose une question. Pour moi, l'intérêt de séparer dans des threads, c'est de pouvoir exectuer en parallèle des taches. Or ici, tes 2 threads ne travaillent jamais en même temps (le thread 1 se met en attente et lance le thread 2, puis le thread 2 se met en attente et relance le thread 1).
    Pourquoi ne pas faire un seul thread "lecture et traitement des données" ?
    Tu n'aurais plus de problème de synchronisation.

    Une autre méthode, permettant de faire de la lecture et du traitement de données en parallèle, serait d'avoir un QQueue (http://qt.developpez.com/doc/latest/qqueue.html) comme tampon temporaire. Ton thread 1 lit les données et stocke dans la QQueue. Ton thread 2 regarde si des données sont présentes dans la QQueue, les traite puis regarde de nouveau si des données sont encore présentes.
    Tu peux utiliser soit un QTimer pour que le thread 2 regarde toute les x millisecondes si des données sont présentes, soit ton thread 1 envoie un signal qui lance le traitement si celui-ci n'est pas en cours.

    En espérant que ces idées puissent t'aider un peu

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 17
    Points
    17
    Par défaut
    Je te remercie pour ta réponse. Je peux éventuellement repenser le mode de fonctionnement. Cependant, si la lecture de fichier est un mode de fonctionnement du Thread 1, ce n'est pas le seul. Dans l'autre mode les données sont lues régulièrement (rythme moindre que la lecture de fichier) d'un périphérique et envoyées indépendamment de l'état du Thread de traitement.
    Pour le mode de lecture de fichier, je ne souhaite pas tout lire d'un coup car les fichiers peuvent être conséquents. Il devrait donc y avoir une synchronisation entre la lecture et le traitement.

  4. #4
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Salut.
    Ce qui m'étonne me plus, c'est que tous ne se bloque pas dés le début....
    Quand tu fait un lock ca bloqua la thread et son eventloop aussi. Donc, ses slots ne seront pas exécutés(si tu est connecté en mode auto).

    Voici deux solutions pour ton problème :

    http://qt.developpez.com/doc/latest/...onditions.html
    http://qt.developpez.com/doc/latest/...emaphores.html

    Tu peut aussi regardai ici :
    http://yan-verdavaine.developpez.com...u.php?id=start
    C'est un article en cours d'écriture, donc pas finie mais j'ai re-exepliqué un peut.

    QQueue ne sera pas adapté car tu va y faire des accès concurrents avec tes deux threads

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 17
    Points
    17
    Par défaut
    Merci pour les infos,
    je suis un peu juste sur ces aspects de connexion. Disposes-tu de liens sur la façon de réaliser l''initialisation de ces différents types de connexion (directes, automatiques, event loop, ...) afin que je sache qui est exécuté où et quand ? Est-ce que ça dépend juste de l'endroit où on réalise le connect ?

    Les connections sont réalisées en dehors des fonctions run des Threads. Les emit sont dans les fonctions run mais avant les wait...

  6. #6
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Cédric-29 Voir le message
    Disposes-tu de liens sur la façon de réaliser l''initialisation de ces différents types de connexion (directes, automatiques, event loop, ...) afin que je sache qui est exécuté où et quand ? Est-ce que ça dépend juste de l'endroit où on réalise le connect ?
    Toute la doc de Qt et la FAQ de Qt
    En gros, ça dépend de l'appartenance d'un QObject à un thread.

    Je croie que j'ai dit une connerie pour les slots. Ils sont bien exécuté car les slots d'un QThread ne sont pas éxécuté dans le thread qu'il interface mais dans celui qui as créé ton QTHread (surement le thread principale)

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 17
    Points
    17
    Par défaut
    Oui, ils sont créés dans le thread principal.

    Le thread 2 continue sa route (entre le emit et le wait) seulement après que le Thread 1 se soit mis au repos !

    En gros, le emit du thread 2 lance d'une traite, l'exécution du slot de réveil du thread 1, réveille et exécute le code du thread 1 (plusieurs tours de boucle de lecture dans un fichier) puis le réendort. Seulement là, on passe à la ligne de code d'après dans le thread 2, pour se mettre au repos (trop tard !).

    C'est peut être un fonctionnement normal ce qui veut dire que mon principe de réalisation est à revoir...

  8. #8
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Cédric-29 Voir le message
    C'est peut être un fonctionnement normal ce qui veut dire que mon principe de réalisation est à revoir...
    As tu regardé les liens que je t'ai donnée????

  9. #9
    Membre actif
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    188
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 188
    Points : 248
    Points
    248
    Par défaut
    bonjour,

    Citation Envoyé par Cédric-29 Voir le message
    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
    Thread 1 :
    
    debut boucle
       // lecture de données
    
       emit reveilThread2(&données)
    
       waitMutex1.lock();
       waitCondition1.wait(&waitMutex1)
       waitMutex1.unlock();
    fin boucle
    
    Thread 2 :
    
    début boucle
       si données
       // Traitement des données
       sinon
       {
       emit reveilThread1()   
       
       waitMutex2.lock();   
       waitCondition2.wait(&waitMutex2)
       waitMutex2.unlock();
       }
    fin boucle
    As tu une raison pour utilisé des "emit" au lieu de d'utiliser "wake" ?
    Pour éviter le problème que tu rencontre il faut que tu utilise le même mutex dans les 2 threads et que tu le lock avant d'utiliser wake (ou emit) :

    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
    Thread 1 :
    
    debut boucle
       // lecture de données
    
       waitMutex.lock();
       waitCondition2.wakeAll();
    
       waitCondition1.wait(&waitMutex1)
       waitMutex.unlock();
    fin boucle
    
    Thread 2 :
    
    début boucle
       si données
       // Traitement des données
       sinon
       {
       waitMutex.lock();   
       waitCondition1.wakeAll();
       
       waitCondition2.wait(&waitMutex2)
       waitMutex.unlock();
       }
    fin boucle
    comme sa quand ton premier thread lance wake le deuxième se réveille mais bloque pour prendre le mutex car c'est toujours le premier qui le détient.
    une fois que le premier est endormit il libère le mutex et le deuxième thread reprend.
    ainsi tu es sur que ton thread sera endormis quand l'autre démarrera.

  10. #10
    Membre actif
    Homme Profil pro
    Freelance
    Inscrit en
    Décembre 2003
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Freelance

    Informations forums :
    Inscription : Décembre 2003
    Messages : 423
    Points : 259
    Points
    259
    Par défaut
    Salut,

    Je viens juste pour confirmer ce que dit atttchoum à une remarque près : le problème du QMutex c'est que
    Attempting to unlock a mutex in a different thread to the one that locked it results in an error
    Source : documentation officielle
    soit, en français, que si tu essaies de déverouillé un mutex depuis un thread différent de celui qui l'a verrouillé => tu vas avoir une erreur.

    Je suis en train de faire quelque chose d'identique à toi (dans le principe pas au niveau du traitement ni quoique ce soit), et la solution qui m'est finalement venu en tête était d'émettre un signal mais uniquement après avoir été capable de prendre un jeton sur un QSemaphore (ce qui est la même chose que ce que te propose atttchoum avec un QMutex - qu'il faudrait donc remplacer par un QSemaphore - et un QWaitCondition). La différence avec ce que te propose atttchoum c'est que dans un cas Thread1 et Thread2 doivent partager deux objets, dans l'autre ils partagent un seul objet mais une connexion supplémentaire doit être établie.
    D'où vient le jeton ? du Thread réveillé par le signal émis. En très simplifié cela donne quelque chose comme ça :


    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
    // Semaphore S1 doit être accessible depuis les deux threads
    
    Thread 1
    
    forever()
    {
         Prendre un jeton dans Semaphore S1
         Récupérer données()     
         emit Signal()
    }
    
    
    Thread 2
    slot() // appelé suite à l'émission de Thread1::signal()
    {
         Déposer jeton dans Semaphore S1 (identique à celui de Thread1)
         Traiter données
    }
    L'avantage de cette méthode est que, d'une part tes threads s'exécutent réellement en parallèles, et d'autres part, si demain le temps de traitement ou le temps de récupération des données devient plus long que l'autre, l'application devrait toujours continuer à fonctionner identiquement.

    Bonne suite
    "La théorie, c’est quand on sait tout et que rien ne fonctionne. La pratique, c’est quand tout fonctionne et que personne ne sait pourquoi. Ici, nous avons réuni théorie et pratique : rien ne fonctionne ... et personne ne sait pourquoi !" et malheureusement c'est souvent le cas en Développement...

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    198
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 198
    Points : 101
    Points
    101
    Par défaut
    La lecture de cette discussion m'interpelle sur deux points :

    1) Je croyais que les signaux ne fonctionnaient pas entre threads !
    Où placez-vous le connect ?

    2) Cédric-29 comment fais-tu pour transmettre tes données entre les deux threads ?

  12. #12
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Bonjour DSGSLA

    Il est bien précisé dans la doc que les signaux/slots de Qt sont thread-safe (et c'est l'une des grandes forces des signaux/slots de Qt). Regarde la discussion suivante pour plus de détails sur le fonctionnement : http://www.developpez.net/forums/d92...te-thread-non/

    Pour transmettre des données entre 2 threads, il suffit simplement de transmettre un pointeur ou une référence aux threads. On ajoute également un système de protection des données (par exemple un QMutex) pour éviter que les 2 threads écrivent en même sur les données

  13. #13
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Pour transmettre des données entre 2 threads, il suffit simplement de transmettre un pointeur ou une référence aux threads.
    Depuis Qt 4.4, QThread lance une eventloop par defaut.
    tu peut donc créer un QOBject qui fait ton traitement et utiliser moveToThread.
    Ça simplifie les connexions et évite de se trimballer un pointeur ou référence.


    Citation Envoyé par gbdivers Voir le message
    On ajoute également un système de protection des données (par exemple un QMutex) pour éviter que les 2 threads écrivent en même sur les données
    Pas forcement, tous dépend du besoin (comme toujours). Qt est basé sur le COW, donc la copie d'un object Qt est quasi null. Et c'est bien plus sure dans un context multithread.
    Imagine, tu emet un pointeur et détruit l'objet. Comme le slot dans un autre thread sera exécuté de manière asynchrone, l'objet pointé peut déjà être détruit => erreur mémoire.


    Un petit exemple intéressant :
    http://qt.developpez.com/doc/latest/...ustomtype.html

Discussions similaires

  1. question: Synchronisation de threads
    Par remimichot dans le forum Concurrence et multi-thread
    Réponses: 2
    Dernier message: 23/07/2006, 18h27
  2. Question sur la synchronisation des threads.
    Par sebastieng dans le forum Langages de programmation
    Réponses: 4
    Dernier message: 07/12/2005, 15h55
  3. Réponses: 1
    Dernier message: 23/05/2005, 15h52
  4. Synchronisation de thread
    Par declencher dans le forum Langage
    Réponses: 2
    Dernier message: 07/01/2004, 10h28

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