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 :

[mutex et ofstream] erreur


Sujet :

C++

  1. #1
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut [mutex et ofstream] erreur
    bonjour

    je voudrais faire la chose suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    mutex m;
    m.lock();
    cout << "alpha";
    m.lock();
    cout << "beta";
    m.unlock()
    dans la réalité quand je met un << endl il fait l'unlock

    mais ce que je comprend pas c'est que dans le même thread (l'application j'ai une erreur sur le cout << "beta", je pensais naïvement qu'on pouvait dans le même thread prendre plusieurs fois le mutex et le libérer une seule fois
    des que j'en prend deux ca coince

    pour expliquer j'aurais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void funcA(){
    m.lock();
    cout << "alpha";
    }
    void funcB(){
    m.lock();
    cout << "beta";
    }
    void funcEnd(){
    m.unlock();
    }
    avec un trhead qui peut appeler funcA puis FuncB puis FuncEnd
    un autre FuncB puis FuncEnd
    et un troisième funcA puis FuncEnd

    dans la réalité j'ai surchargé l'operateur << dans un classe donc je sais pas vraiment dans quelle surcharge on passera en premier

    vous avez une idée ?

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    euh je crois que j'ai trouvé en utilisant plutot un recursive_mutex
    sauf que
    si je fais deux lock()
    il faut que je fasse deux unlock()

    y'a t-il un moyen de faire autant de lock qu'on veut et un seul unlock ?

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Citation Envoyé par ikeas Voir le message
    euh je crois que j'ai trouvé en utilisant plutot un recursive_mutex
    sauf que
    si je fais deux lock()
    il faut que je fasse deux unlock()

    y'a t-il un moyen de faire autant de lock qu'on veut et un seul unlock ?
    Non. Et c'est incontournable.
    Le principe d'un mutex, c'est : à chaque lock correspond un unlock.
    On ne peux faire qu'un seul lock sur un mutex, sauf le récursif qui permet à un même thead de faire plusieurs locks.

    Attention, on ne soit jamais utiliser le lock directement. C'est comme pour le new. Si on l'utilise a la certitude à 99% de faire une connerie. Il faut utiliser un objet propriétaire du lock, la destruction de l'objet fera le unlock.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     {
        std::lock_guard<std::mutex> lock(mon_mutex);  // lock du mutex
     
        std::cout << "hello";
     }                                 // unlock du mutex à la disparition de l'objet lock

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    merci pour le retour mais si j'utilise ton code avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    {
        std::lock_guard<std::mutex> lock(mon_mutex);  // lock du mutex
     
        std::cout << "hello";
     }
    je n'aurais pas le comportement que je veux qui est
    f1() { lock(); }
    f2() { lock(); }
    f3() { lock(); }
    fend() { unlock(); }
    et l'utilisateur peut appeler comme il sent f1 et/ou f2 et/ou f3 dans n'importe quel ordre puis fend() pour libérer les autres thread qui sont bloqués sur le lock

    pour le moment j'ai résolu les unlock multiple a faire en comptant les lock

    pour être précis j'ai fait une class Logger qui se comporte comme un stream
    avec les surcharges << de tous les types que j'utilise
    si tu veux je te filerais le code

  5. #5
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    un mutex est fait fait pour protéger des actions ultra courtes car si ça dure, on peut bloquer plein de threads pendant tout ce temps. En temps réel on considère long ce qui se compte en microsecondes.
    Ici chaque thread devrait avoir son propre flot. Et au moment du fend() on transfère le flot local dans le flot commun et on ne fait que ça sous mutex.

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    oui je sais bien j'utilise ce mécanisme par qu'on a une ligne du style
    clogger << "info = " << toto << " resultat = " << iResult << endl;
    et que ca ne va servir qu'en debug et pas en exploitation ....

    je sais ce ne sont pas vraiment des zone d'exclusion le thread safe est juste la pour pas enregistrer des information concurrentes et pour faire un file logger simple

  7. #7
    Membre Expert
    Femme Profil pro
    ..
    Inscrit en
    Décembre 2019
    Messages
    692
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 95
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

    Informations forums :
    Inscription : Décembre 2019
    Messages : 692
    Par défaut
    Citation Envoyé par ikeas Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void funcA(){
    m.lock();
    cout << "alpha";
    }
    void funcB(){
    m.lock();
    cout << "beta";
    }
    void funcEnd(){
    m.unlock();
    }
    avec un trhead qui peut appeler funcA puis FuncB puis FuncEnd
    un autre FuncB puis FuncEnd
    et un troisième funcA puis FuncEnd

    dans la réalité j'ai surchargé l'operateur << dans un classe donc je sais pas vraiment dans quelle surcharge on passera en premier

    vous avez une idée ?
    Bonjour,

    Il te faut une porte d'entrée, FuncBegin().
    Assure-toi donc que chaque thread commence par appeler FuncBegin(), tu mettras ton lock seulement dans cette fonction. Disons que le plus important est que le lock soit en amont, c'est tout, voire au niveau des thread mêmes.

  8. #8
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    je te file le code tu verra ca marche pas trop mal
    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
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    #pragma once
     
    // pour creer un logger sur un autre fichier faire
    // extern Logger xlogger("nom du fichier");
     
    // pour changer le format d'affichage de l'heure
    // extern Logger xlogger("nom du fichier", "format"); ou
    // extern logger xlogger(string(), "format");  
     
    // le format est au standard strftime
    // http://www.cplusplus.com/reference/ctime/strftime/
    // avec mmm pour les millisecondes
     
     
    #include <iostream>
    #include <iomanip>
    #include <locale>
    #include <vector>
    #include <algorithm>
    #include <functional>
    #include <fstream>
    #include <mutex>
    #include <shared_mutex>
    #include <ctime>
    #include <string.h>
    #include <sstream>
     
    #define DEFAULT_LOGGER_FILE_NAME "c:\\test2.txt"   // nom du fichier log par defaut
    #define DEFAULT_FOMRAT_TC "%H:%M:%S.mmm"           // format de la date/heure (ici juste heure.milliseconde)
     
    #define FORMAT_MILL "mmm"  // indicateur de format pour les milliseconde (NE PAS MODIFIER)
     
    #ifdef WIN32
    #define localtime_r(_Time, _Tm) localtime_s(_Tm, _Time)
    #endif
     
    // nom du fichier
    #define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
     
    // nom de la fonction
    #ifndef __FUNCTION_NAME__
    #if defined __func__ 
    // Undeclared 
    #define __FUNCTION_NAME__   __func__ 
    #elif defined __FUNCTION__ 
    // Undeclared
    #define __FUNCTION_NAME__   __FUNCTION__  
    #elif defined __PRETTY_FUNCTION__
    // Undeclared
    #define __FUNCTION_NAME__   __PRETTY_FUNCTION__
    #else
    // Declared
    #define __FUNCTION_NAME__   "N/A"   
    #endif // __func__ 
    #endif // __FUNCTION_NAME__
     
    // simplifications
    #define _LI_      __LINE__             // numero de ligne dans le fichier
    #define _FN_      __FUNCTION_NAME__    // nom de la fonction
    #define _FI_      __FILENAME__         // nom du fichier
     
    using namespace std;
    using namespace std::chrono;
     
    class Logger
    {
    #pragma region private
    private:
    	int lockcount = 0;
    	ofstream ofs;
    	recursive_mutex m;
    	string format;
    	void open(string filename) {
    		// ce code append ne met pas a jour le fichier immediatement
    		//ofs.open(LOGGER_FILE_NAME, std::ofstream::out | std::ofstream::app);
    		ofs.open(filename, std::ofstream::out);
    	}
    	void close() {
    		ofs.close();
    	}
    	void lock() {
    		m.lock();
    		// ajoute la date et l'heure sur lockcount = 0
    		if (lockcount == 0)
    		{
    			ofs << "[" << getTime() << "] ";
    		}
    		lockcount++;
    	}
    	void unlock() {
    		for (; lockcount != 0; m.unlock(), --lockcount);
    	}
    	//says does b contain a
    	bool isPartOf(char *a, char *b) {
    		return (strstr(b, a) != NULL);
    	}
    	const long millisecond() {
    		tm localTime;
    		system_clock::time_point t = system_clock::now();
    		time_t now = system_clock::to_time_t(t);
    		localtime_r(&now, &localTime);
     
    		const duration<double> tse = t.time_since_epoch();
    		seconds::rep milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(tse).count() % 1000;
    		return milliseconds;
    	}
    	const string getTime() {
    		struct tm time_info;
    		time_t time_create = time(NULL);
    		localtime_s(&time_info, &time_create);
    		char timebuf[80];
    		strftime(timebuf, 80, format.c_str(), &time_info);
    		string time_ret(timebuf);
    		if (isPartOf((char*)FORMAT_MILL, timebuf))
    		{
    			auto milli = millisecond();
    			ostringstream stringStream;
    			stringStream << setfill('0') << setw(3) << milli;
    			time_ret.replace(time_ret.find(FORMAT_MILL), sizeof(FORMAT_MILL) - 1, stringStream.str());
    		}
    		return time_ret;
    	}
    #pragma endregion private
     
    #pragma region public
    public:
    	Logger(string filename = DEFAULT_LOGGER_FILE_NAME,
    	        string formatDateTime = DEFAULT_FOMRAT_TC) : format(formatDateTime){
    		if (filename.empty()) filename = DEFAULT_LOGGER_FILE_NAME;
    		open(filename);
    	}
    	inline Logger &operator<<(const char *val) {
    		return operator<<(std::string(val));
    	}
    	inline Logger &operator<<(const std::string &val) {
    #ifdef LOGGER	
    		lock();
    		ofs << val;
    		ofs.flush();
    #endif
    		return *this;
    	}
    	inline Logger &operator<<(const int val) {
    #ifdef LOGGER
    		lock();
    		ofs << val;
    		ofs.flush();
    #endif
    		return *this;
    	}
    	inline Logger &operator<<(const bool val) {
    #ifdef LOGGER
    		lock();
    		ofs << val;
    		ofs.flush();
    #endif
    		return *this;
    	}
    	inline Logger &operator<<(std::ostream&(*val)(std::ostream&)) {
    #ifdef LOGGER
    		lock();
    		ofs << val << flush;
    		//close();
    		//open();
    		unlock();
    #endif
    		return *this;
    	}
    #pragma endregion public
    };
     
    extern Logger clogger;
    et c'est surtout simple

    pour l'activer faut mettre LOGGER dans les symboles de compilation evidement

  9. #9
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par défaut
    J'ai l'impression que c'est extrêmement fragile, vu que tu déverrouilles sur n'importe quel manipulateur de flux, pas seulement endl!

    Je pense que ton problème principal c'est que tu as fait tes modifications sur la mauvaise couche. Ce n'est pas le stream lui-même qu'il faut modifier, mais le streambuf: Ainsi, tu pourras utiliser un buffer interne pour accumuler les caractères jusqu'à ce que sync() soit appelé (ou jusqu'à ce qu'un \n soit reçu si tu veux faire ta synchronisation ligne à ligne) et c'est lors du sync() que tu utilises le mutex...

    PS: Ceci n'a rien à voir avec le langage C++/CLI. Il faudrait déplacer le message.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Si tu veux mon avis, tu fais les choses bien plus compliquées qu'elles ne devraient être, parce que tu les fais quelques peu à l'envers.

    Car, si j'ai bien compris, la situation dans laquelle tu te trouves, c'est d'avoir plusieurs threads qui devront envoyer un log en mode débug, et dont tu voudrais éviter que l'écriture ne se mélange, justement, à cause de la présence des threads.

    Dés lors, au lieu d'envoyer directement toutes les données de tes thread vers le flux de sortie, pourquoi ne placerais tu pas ces données dans une file d'attente qui -- elle -- serait protégée par un mutex, de manière à ce qu'un thread ne puisse pas essayer d'envoyer ses données en même temps qu'un autre

    Cela te permettrait d'envoyer les données "dans l'ordre d'arrivée" sans avoir à t'inquiéter de l'ordre dans lequel elles arrivent tant que ta pile ne serait pas vide

    Tu aurais donc quelque chose ressemblant à
    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
    class LogQueue{
    public:
        void lock(){
            mutex_.lock();
        }
        void unlock(){
            mutext_.unlock();
        }
        /* pour les chaines de caractères */
        void add(std::string const & str){
             datas_.push(str);
        }
        /* pour les types primitifs */
        template <typename T>
        void add(T value){
            datas.push(std::to_string(value));
        }
        void endOfLine(){
            datas_.push("\n");
        }
        bool empty() const{
            return datas_.empty();
        }
        void write(std::ostreamd & ofs){
            ofs<< datas_.top();
            datas_.pop();
        }
    private:
        std::mutex mutex_;
        std::queue<std::string> datas_;
    };
    (ce code est écrit en quatrième vitesse et n'a pas été testé. Il peut contenir quelques erreurs )

    Et, d'un autre coté, ta classe logger pourrait prendre une forme proche de

    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
    class Logger{
    public:
         template <typename ... Datas>
         write(Datas ... d){
              queue_.lock();
              add(d) ...:
              queue_.endOfLine();
              queue_.unlock();
              send();
         }
    private:
         template <typename T, typename ... Datas>
         void add(T first, Datas ... next){
             queue_.add(first);
             add(next)...;
         }
         void send(){
             while(! queue_.empty())
                 queue_.write(output_):
         }
         std::ostream output_;
         LogQueue queue_;
    };
    (ici aussi, il y aura peut-être bien quelques corrections à faire, cependant, l'idée générale est donnée )

    De cette manière tu protège l'ajout des données dans la pile par un mutex, ce qui empêchera deux threads d'essayer d'envoyer leur données en même temps, mais, d'un autre coté, dés qu'il y a eu des données envoyées, ton logger va réellement écrire l'ensemble des informations que ta pile contient, quel que soit le thread qui aura été à l'origine de leur ajout dans la pile
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. erreur avec ofstream (ios is private)
    Par luckyalan dans le forum C++
    Réponses: 7
    Dernier message: 19/06/2020, 15h50
  2. Réponses: 4
    Dernier message: 03/10/2008, 14h29
  3. Erreur avec : ofstream en paramètre d'une fonction
    Par droledelolo dans le forum C++
    Réponses: 4
    Dernier message: 21/08/2008, 09h04
  4. ofstream: detecter les erreurs d'ecriture
    Par Romain93 dans le forum SL & STL
    Réponses: 5
    Dernier message: 27/04/2008, 13h09
  5. [C#] Erreur de mutex ?!
    Par NoiBe dans le forum Services Web
    Réponses: 2
    Dernier message: 29/03/2007, 12h16

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