2010-12-15 6 views
2

Quelle est la manière la plus facile de créer mon propre std::cerr afin qu'il soit threadsafe ligne par ligne.Un thread-safe thread-safe std :: cerr pour C++

Je cherche de préférence le code pour le faire. Ce dont j'ai besoin est que a line of output (terminé avec std :: endl) généré par un thread reste as a line of output quand je le vois réellement sur ma console. (Et n'est pas mélangé avec la sortie d'un autre thread)

SOLUTION: std::cerr est BEAUCOUP plus lent que cstdio. Je préfère utiliser fprintf(stderr, "The message") à l'intérieur d'une classe CriticalSectionLocker dont le constructeur acquiert un verrou thread-safe et que le desturctor le libère.

+1

Comment imaginez-vous la création d'un tampon local de fil "accroché" dans 'std :: cerr' réduirait la mise en mémoire tampon sur un tampon local de thread "outside", puis écrirait des lignes entières dans 'std :: cerr'? Un tampon est un tampon. 'std :: ostringstream' est une approche typique à usage général pour cela. –

+3

Êtes-vous par hasard à la recherche d'une bibliothèque de journalisation thread-safe? – yasouser

+1

J'ai récemment pris connaissance du projet log4cpp (http://log4cpp.sourceforge.net/). Vous ne savez pas si c'est ce que vous cherchez!?!? Peut-être utile de vérifier. – yasouser

Répondre

3

Ce:

#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;} 

fonctionne sur la plupart des compilateurs pour le cas commun de myerr("ERR: " << message << number).

+0

Cela présume que vous ne voulez pas utiliser l'opérateur '' 'pour le formatage (par exemple' cerr << "Ce message de journal a été imprimé" << nfois << "fois. << << endl;') –

+2

Hrm. J'ai vraiment tort à ce sujet, parce que j'ai oublié temporairement comment fonctionnent les macros. Tout ce que vous devez faire pour obtenir la mise en forme est l'appel 'lerr (" Ce message de journal a été imprimé "<< nfois <<" fois. ")' Et la substitution de texte le ferait correctement. –

5

Ce n'est pas le cas. Il n'y a rien sur std::cerr (ni aucun autre objet partagé dans la langue entière) qui peut savoir quel thread fait un appel particulier au << operator pour empêcher l'entrelacement.

Le mieux que vous pouvez faire est de créer un tas d'objets de l'enregistreur spécifiques au thread que chaque accumuler des données jusqu'à ce qu'un saut de ligne est atteinte, un verrou qui protège cerr de manière à écrire une ligne entière à la fois à cerr tout en maintenant la fermer à clé.

+0

J'avais l'intention d'utiliser ma propre classe. Comment écrire ces objets de journalisation basés sur iostream que vous décrivez? c'était ma question originale. Je ne sais rien à propos de iostreams autres que cerr << "message" – unixman83

+0

Que je ne suis pas si sûr. Je pense qu'il y a plusieurs options: # 1 vous pouvez écrire votre propre streambuf qui vide les données dans 'cerr' et utiliser un ostream normal pour formater les données, # 2 Vous pouvez utiliser un' std :: stringstream' dans le logger de chaque thread pour accumuler les données (le stringstream est complètement compatible avec toutes les primitives de mise en forme de 'cerr', mais les exposer est plus de travail). Où vous obtenez un verrou pour vider à cerr sera spécifique à la plate-forme. # 3 vous pouvez faire quelque chose avec 'sprintf' et des chaînes de mise en forme à la place. –

+0

Ou obtenir quelque chose comme log4cpp –

2

Une amélioration (qui ne rentre pas vraiment dans un commentaire) sur l'approche dans le commentaire d'unixman.

#define LOCKED_ERR \ 
    if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \ 
    else std::cerr 

qui peut être utilisé comme

LOCKED_ERR << "ERR: " << message << endl; 

si ErrCriticalSectionLocker est mis en œuvre avec soin. Mais, personnellement, je préfère la suggestion de Ken.

+0

+1 pour une utilisation créative du cycle de vie des temporaires. Pourriez-vous fournir une implémentation de 'ErrCriticalSectionLocker', car la sémantique est * très * non triviale? –

+0

Hmm. Je vois; Vous pouvez utiliser cette méthode sans *** 'parenthesis' *** mais je vois peu de chose là-dessus, puisque mon style de programmation C (je viens du C-background) appelle généralement des parenthèses. En outre, 'ErrCriticalSectionLocker' est confus ... – unixman83

4

Voici une solution de journalisation basée sur les threads sûrs que j'ai cuits à un moment donné. Il utilise boost mutex pour la sécurité des threads. Il est un peu plus compliqué que nécessaire parce que vous pouvez brancher dans les politiques de sortie (si il aller à un fichier, stderr, ou ailleurs?):

logger.h:

#ifndef LOGGER_20080723_H_ 
#define LOGGER_20080723_H_ 

#include <boost/thread/mutex.hpp> 
#include <iostream> 
#include <cassert> 
#include <sstream> 
#include <ctime> 
#include <ostream> 

namespace logger { 
    namespace detail { 

     template<class Ch, class Tr, class A> 
     class no_output { 
     private: 
      struct null_buffer { 
       template<class T> 
       null_buffer &operator<<(const T &) { 
        return *this; 
       } 
      }; 
     public: 
      typedef null_buffer stream_buffer; 

     public: 
      void operator()(const stream_buffer &) { 
      } 
     }; 

     template<class Ch, class Tr, class A> 
     class output_to_clog { 
     public: 
      typedef std::basic_ostringstream<Ch, Tr, A> stream_buffer; 
     public: 
      void operator()(const stream_buffer &s) { 
       static boost::mutex mutex; 
       boost::mutex::scoped_lock lock(mutex); 
       std::clog << now() << ": " << s.str() << std::endl; 
      } 

     private: 
      static std::string now() { 
       char buf[64]; 
       const time_t tm = time(0); 
       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tm)); 
       return buf; 
      } 

     }; 

     template<template <class Ch, class Tr, class A> class OutputPolicy, class Ch = char, class Tr = std::char_traits<Ch>, class A = std::allocator<Ch> > 
     class logger { 
      typedef OutputPolicy<Ch, Tr, A> output_policy; 
     public: 
      ~logger() { 
       output_policy()(m_SS); 
      } 
     public: 
      template<class T> 
      logger &operator<<(const T &x) { 
       m_SS << x; 
       return *this; 
      } 
     private: 
      typename output_policy::stream_buffer m_SS; 
     }; 
    } 

    class log : public detail::logger<detail::output_to_clog> { 
    }; 
} 

#endif 

Utilisation ressemble à ceci:

logger::log() << "this is a test" << 1234 << "testing"; 

note l'absence d'un '\n' et std::endl puisqu'il est implicite. Les contenus sont mis en mémoire tampon puis sortis de façon atomique à l'aide de la stratégie spécifiée par le modèle. Cette implémentation ajoute également un horodatage à la ligne, car elle sert à la consignation. La politique no_output est strictement facultative, c'est ce que j'utilise lorsque je veux désactiver la journalisation.

3

Pourquoi ne pas simplement créer une classe de verrouillage et l'utiliser là où vous voulez faire des E/S sécurisées?

class LockIO 
{ 
    static pthread_mutex_t *mutex; 
public: 
    LockIO() { pthread_mutex_lock(mutex); } 
    ~LockIO() { pthread_mutex_unlock(mutex); } 
}; 

static pthread_mutex_t* getMutex() 
{ 
    pthread_mutex_t *mutex = new pthread_mutex_t; 
    pthread_mutex_init(mutex, NULL); 
    return mutex; 
} 
pthread_mutex_t* LockIO::mutex = getMutex(); 

Ensuite, vous mettez une IO que vous voulez dans un bloc:

std::cout <<"X is " <<x <<std::endl; 

devient:

{ 
    LockIO lock; 
    std::cout <<"X is " <<x <<std::endl; 
} 
Questions connexes