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 :

Explosion de la consommation mémoire


Sujet :

Multithreading

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut Explosion de la consommation mémoire
    Bonjour,

    J'ai un soucis avec QThread. Voilà le soucis:

    Je suis en train d'écrire une système de log avec architecture : un LogManager qui contient les fonctions de log ( trace, warning , ... ) et une collection d'"engine" qui eux s'occupe d'écrire les logs proprement dit.

    Les engines tourne dans un thread, chacun le leur et la communication entre le LogManager et les engines se fait via les signaux/slots.

    Cependant j'ai un soucis, la mémoire vive utilisé par l'application explose et très rapidement ( avec une boucle infinie avec un trace("TEST") et 4 engines => 15 secondes = 1Go5 de mémoire prise ). J'ai donc pensé a une leak, sauf que il n'y a pas d'allocation dynamique dans le code.
    Ci dessus des extraits de code:

    La méthode pour ajouter un engine:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void LogManager::addLoggerEngine(QString engineName, LoggerEngine* engine)
    {	
    	m_loggerEngines.insert(engineName, engine);	
    	engine->moveToThread(thread); /* Work but cause a memory leak, had to fix that */
    	connect(this, SIGNAL(logMessage(LogManager::LogLevel, QList<QVariant>)), engine, SLOT(writeFormatted(LogManager::LogLevel, QList<QVariant>)));	
    	thread->start(); /* line that cause leak concern QDateTime::currentDate().ToString("format") */
     
    	emit loggerEngineAdded(engineName);
    }
    La méthode pour loguer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void LogManager::log ( LogLevel level, const QList<QVariant> & args )
    {	
    	emit logMessage(level, args);
    }
    Prototype du signal:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void logMessage(LogManager::LogLevel level, QList<QVariant> args);
    Un des slots: ( tous on le même soucis )

    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
    void ConsoleLoggerEngine::writeFormatted(LogManager::LogLevel level, QList<QVariant> messages )
    {
     
    	if(!m_isEnabled) return;
    	if (messages.isEmpty()) return;
    	if(!isLogLevelEnabled(level)) return;
     
    	QString header = '[' + QTime::currentTime().toString("hh:mm:ss.zzz") + "] [" + LogManager::logLevelToString(level) + "] ";
        QString padding;
     
        *stdstream << header;
        padding.append(' ');
        int count = 0;
     
        Q_FOREACH(const QVariant& out, messages)
        {
            if (!out.isNull())
            {
                if (count != 0) *stdstream << padding;
                *stdstream << out.toString();
            }
            count++;
        }
        *stdstream << endl;
    }
    En sachant que si je commente ce code = plus de soucis

    Cordialement Mathieu

  2. #2
    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.
    C'est quoi stdstream ?

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Bonjour
    stdstream est un QTextStream binder sur la sortie console.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ConsoleLoggerEngine::ConsoleLoggerEngine(QObject *parent) : LoggerEngine(parent)
    {	
    	stdstream = new QTextStream(stdout);
    }
     
    ConsoleLoggerEngine::~ConsoleLoggerEngine()
    {
    	delete stdstream;
    }

  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
    Comment voie tu la mémoire utilisé?
    Es ce que ton appli crash a cause d'un manque de mémoire?

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    J'utilise le gestionnaire de tache. Je sais bien que ce n'est pas forcément très précis mais je vois la mémoire vive monté très rapidement donc pas on signe ^^.

    Si j'attend , l'application ne plante pas , elle ralenti mais je suis cappé en RAM utilisé ( 8Go dont 6Go5 par l'application si j'en crois le gestionnaire des taches )

  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 lange59 Voir le message
    J'utilise le gestionnaire de tache. Je sais bien que ce n'est pas forcément très précis mais je vois la mémoire vive monté très rapidement donc pas on signe ^^.
    ^^

    Bizarre. Car dans le code que tu donne, il n'y as rien de spéciale.
    Tu es sur que c'est lié à ton slot?
    N'aurais tu pas une sorte à ton slot de manière récursive?
    Que contient QList<QVariant>?

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    QList<QVariant> contient des QVariant qui correspondande au message a log, dans le cas présent une QString = "TEST" mais ca peut être n'importe quoi.

    C'est forcement lié au slot étant donné que si je le commente, donc il est appelé mais sans rien faire = plus de problèmes.
    De même si je supprime le Qtime::currentTime().toString(""), plus de soucis.
    Si encore je pouvais m'en passer mais bon j'ai besoin de la date ^^.

    J'ai pas pensé a une appel récursif, je vais regarder mais le code est vraiment triviale , ya rien de complexe dedans.
    Un soucis de polymorphisme peut etre ?

  8. #8
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Désolé pour le double post, voici un code qui permet de reproduire l'erreur :

    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
     
     
    #include <QObject>
     
    class Receiver : public QObject
    {
    	Q_OBJECT
    public:
    	Receiver() {}
    	~Receiver() {}
     
    public slots:
    	void Test()
    	{
    		QString header = "["+QTime::currentTime().toString("hh:mm:ss zzz")+"]";
    		qDebug() << header;
    	}
    };
     
    class Emitter : public QObject
    {
    	Q_OBJECT
    public:
    	Emitter() {}
    	~Emitter() {}
     
    	void Test()
    	{
    		emit SignalTest();
    	}
     
    signals:
    	void SignalTest();
     
    };
     
    #include "main.moc"
     
    int main(int argc, char *argv[])
    {
    	QCoreApplication a(argc, argv);
     
    	Emitter emitter;
     
    	QThread *thread = new QThread(&emitter);
     
    	for(auto i = 0; i < 5; i++)
    	{
    		Receiver *r = new Receiver();
              QThread *thread = new QThread(&emitter);
    		r->moveToThread(thread);
    		QObject::connect(&emitter, SIGNAL(SignalTest()), r,  SLOT(Test()));
    		thread->start();
    	}
     
    	forever
    	{
    		emit emitter.Test();
    	}
     
     
    	return a.exec();
    }
    Si on exécute ça on remarque bien que la mémoire utilisé par l'application augmente de façon très rapide sans qu'il y est de raison.

  9. #9
    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.
    Il manque l'execution de l'eventloop principale : a.exec().

    C'est peut être dû à cela.

    Je n'ai pas d'installe de Qt sous la main pour tester :/

  10. #10
    Membre habitué
    Avatar de bobti89
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    86
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 86
    Points : 150
    Points
    150
    Par défaut
    Il me semble que les timers doivent être exécutés dans le thread principal, le problème vient peut-être de la.

    Regarde ce topic, il peut t'intéresser.

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Bonjour,

    Timer ? Ya pas de timers dans le code.

    Même en ajoutant la boucle principale, cela ne change rien, la mémoire augmente toujours. De toute façon étant donnée que j'utilise un forever, le a.exec() ne sera jamais atteint.
    Mais la doc de Qt précise que un QThread , lance par défaut une boucle d'évement dans le run() et les slots sont correctement appelés dans les Receiver donc c'est que les signaux/slots marche sans soucis.

    C'est assez incompréhensible comme problème :p

  12. #12
    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 lange59 Voir le message
    Même en ajoutant la boucle principale, cela ne change rien, la mémoire augmente toujours. De toute façon étant donnée que j'utilise un forever, le a.exec() ne sera jamais atteint.
    Qt délègue parfois la destruction de mémoire à l'event loop pour ne par retarder un traitement. Si tu ne l'appel pas, tu bloque une partie de QT. D'ou je pense ton problème.

    Pourquoi un forever? Tu peux aussi executer l'eventloop dans ton forever avec
    http://qt-project.org/doc/qt-4.8/qco...#processEvents

  13. #13
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    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
    	QCoreApplication a(argc, argv);
     
    	Emitter emitter;
     
    	QThread *thread = new QThread(&emitter);
     
    	for(auto i = 0; i < 5; i++)
    	{
    		Receiver *r = new Receiver();
              QThread *thread = new QThread(&emitter);
    		r->moveToThread(thread);
    		QObject::connect(&emitter, SIGNAL(SignalTest()), r,  SLOT(Test()));
    		thread->start();
    	}
     
    	forever
    	{
    		emitter.Test();
    		QCoreApplication::processEvents();
    	}
     
     
    	return a.exec();
    Résultat: Aucun changement, la mémoire utilisé monte toujours.
    Je vais finir par croire qu'il y a un soucis du coté des threads avec Qt ...

  14. #14
    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
    tu utilise quelle versionde Qt? tu es sous windows?

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Qt Version 4.8.2, compilé avec MSVC2010 en 64 Bits Shared.
    Le bug se produit aussi en version 32Bits.

  16. #16
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Up.

    Le problème est toujours là. Je commence a croire qu'il y a un soucis de libération de ressources avec le système de Signals/Slots quand on l'utilise pour la connection inter-thread.
    J'ai continué à faire des tests et le problème se repete sans cesse.

    Si quelqu'un a une réponse je suis preneur

    Cordialement lange

  17. #17
    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
    as tu essayé d'avoir a.exec ?

  18. #18
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 42
    Points : 21
    Points
    21
    Par défaut
    Purée ca me rend fou.

    C'était bien ça. J'ai modifier le code et utliser des QTimer pour envoyer les signaux à mes threads et ajouter un a.exec() comme retour du main et là plus de soucis.
    C'est quand même étrange , on dirait que Qt garde en stocks les paramétres de slots et ne les "nettoie" que si une boucle d'evenement est lancé dans le thread principal.
    Le problème c'est que ca n'est indiqué nul part dans la doc de Qt (et je l'ai fouillé de fond en comble ).

    Merci pour le coup de main. Je vais enfin avoir mon système de logs multithreaded .

    EDIT: C'est pas ça le problème, j'ai trouvé d'ou ca vient .

    Je viens a l'instant de m'en rendre compte, si je diminue au mininium la vitesse de tick du QTimer (donc timer.start(): la mémoire utilisé augmente comme pendant le "bug", quand je l'a met a un "niveau raisonnable" ( timer.start(1) ) = plus de soucis.
    C'est donc pas un bug, c est le système d'evenement de Qt qui n'arrive pas gérer aussi vite les evenements donc il les met en queue, d'ou une copie des parametres d'ou une augmentation de la mémoire .

    Exemple de Code:

    La classe Thread est juste une sous classe de QThread qui sert de relais pour les signaux entre le thread principal et une sous classe QObject vivant dans le thread et qui ne fait rien de spécial ( log l'id du thread courant )

    Code qui "leak":
    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
    	Thread thread1;
    	Thread thread2;
    	Thread thread3;
    	Thread thread4;
     
    	thread1.start();
    	thread2.start();
    	thread3.start();
    	thread4.start();
     
    	forever
    	{
    		thread1.TestFonction();
    		thread2.TestFonction();
    		thread3.TestFonction();
    		thread4.TestFonction();
    	}
          return a.exec();
    // return 0; Les deux leak


    Cette version "leak" aussi:

    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
    	Thread thread1;
    	Thread thread2;
    	Thread thread3;
    	Thread thread4;
     
    	thread1.start();
    	thread2.start();
    	thread3.start();
    	thread4.start();
     
    	QTimer timer1;
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread1, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread2, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread3, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread4, SLOT(TestFonction()));
        timer1.start();
     
    return a.exec();
    Ces deux versions ne leak pas:

    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
    	Thread thread1;
    	Thread thread2;
    	Thread thread3;
    	Thread thread4;
     
    	thread1.start();
    	thread2.start();
    	thread3.start();
    	thread4.start();
     
    	QTimer timer1;
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread1, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread2, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread3, SLOT(TestFonction()));
    	QObject::connect(&timer1, SIGNAL(timeout()), &thread4, SLOT(TestFonction()));
        timer1.start(1);
     
     
    	return a.exec();
    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
    Thread thread1;
    	Thread thread2;
    	Thread thread3;
    	Thread thread4;
     
    	thread1.start();
    	thread2.start();
    	thread3.start();
    	thread4.start();
     
    	forever
    	{
    		thread1.TestFonction();
    		thread2.TestFonction();
    		thread3.TestFonction();
    		thread4.TestFonction();
                    Sleep(1);
    	}
          return a.exec();
    Cordialement Mathieu

  19. #19
    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
    lors d'une connection entre thread, un évènement est ajouté à l'eventloop du thread pour executer le slot. Donc si tu en envoie trop tu fais exploser l'eventloop ^^

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

Discussions similaires

  1. Voir la consommation mémoire
    Par MicroPuce dans le forum Général Java
    Réponses: 4
    Dernier message: 10/10/2006, 09h19
  2. [C#] probleme de consommation mémoire
    Par xtream dans le forum Windows Forms
    Réponses: 3
    Dernier message: 21/06/2006, 13h16
  3. [TStringGrid] Consommation mémoire
    Par spender dans le forum Bases de données
    Réponses: 4
    Dernier message: 09/03/2006, 21h48
  4. [Consommation mémoire] Quoi utiliser pour trouver?
    Par doudine dans le forum Interfaces Graphiques en Java
    Réponses: 1
    Dernier message: 25/01/2006, 13h50
  5. Réponses: 4
    Dernier message: 09/11/2005, 13h32

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