2010-08-25 17 views
2

Je suis très nouveau à ce sujet et je m'excuse si ma question n'est pas claire.Besoin de savoir comment vérifier si la chaîne entrée s'est terminée dans un enregistreur thread-safe en C++

J'ai créé un enregistreur de thread sécurisé en C++. Cet enregistreur sera utilisé dans un grand programme & sera appelé à partir de plusieurs endroits. J'utilise un singleton donc il n'y a qu'une seule instance de l'enregistreur. Cet enregistreur génère un fichier & vers la console. Il se comporte comme cout; il prend une chaîne d'un autre fichier (la concatène si nécessaire), stocke les peices dans un tampon jusqu'à ce que la chaîne soit terminée, puis sort avec cout. La chaîne est stockée en tant que const char *. À l'heure actuelle, les mutex sont verrouillés dans une fonction et déverrouillés dans une autre fonction (c'est mon problème) qui surcharge l'opérateur endl.

Mon problème est que cette fonction (où les mutex sont déverrouillés) ne fonctionne que si l'utilisateur écrit endl dans les autres fichiers où l'enregistreur est appelé. J'ai besoin que ce soit un utilitaire polyvalent qui ne dépendra pas de ce que l'utilisateur écrit car un utilisateur ne peut pas utiliser endl ou peut l'utiliser trop souvent. J'ai maintenant besoin de quelques moyens pour mon enregistreur d'identifier quand la chaîne (de l'autre fichier) est faite pour qu'elle puisse vider le tampon. Actuellement, endl est comme un mot-clé & J'ai besoin de quelques moyens pour le faire fonctionner sans mots-clés. Je pensais initialement que je pourrais trouver un moyen de vérifier le caractère de fin "\ 0" dans la chaîne, puis d'utiliser cette vérification pour savoir que la chaîne est terminée et ensuite vider le tampon. Cependant, je reçois des erreurs hors limites quand je fais cela.

Merci pour votre temps

+0

Il serait utile si vous mettez un peu de code source, comme comment l'enregistreur est appelé. Quel est le format général des messages d'erreur et quel type utilisez-vous? De votre question, il pourrait s'agir de std :: string, stream, ou de tableaux de caractères. –

+0

L'enregistreur est conçu pour gérer à la fois std :: string et const char * il gère les deux – joy

Répondre

0

En général, il est une mauvaise idée d'avoir mutex dans une fonction et déverrouiller dans un autre. Il devrait être verrouillé et déverrouillé dans la même fonction.

J'ai créé quelque chose de similaire, et j'ai typiquement créé une classe C++ appelée Error.

Ainsi, l'utilisateur crée un objet Error et cet objet d'erreur gère tous les éléments de terminaison. Ensuite, l'objet d'erreur est envoyé à la file d'attente de l'ErrorLogger et l'enregistreur d'erreurs se termine lorsque la file d'attente ErrorLogger est vide. Alors vous n'avez pas à vous soucier des mutex, car ErrorLogger a le temps de traiter hors de la file d'attente.

+0

Dennis, étant nouveau à cela, je vais regarder dans votre solution proposée et voir si cela peut fonctionner pour moi. Merci pour votre réponse. Cependant, je ne sais pas si je peux me débarrasser des mutex. C'est un enregistreur très complexe, il a beaucoup de fonctionnalités ajoutées que je n'ai pas mentionné ci-dessus. Mais ils doivent être fait entre le verrouillage et le déverrouillage. J'utilise les mutexes ailleurs pour m'assurer que le logger est threadsafe et fonctionnel avec ces fonctionnalités. Néanmoins, j'ai encore besoin de quelques moyens pour savoir quand la chaîne est complète. Comment puis-je vérifier si une chaîne est faite sans mot-clé? – joy

+0

J'ai besoin d'en savoir plus sur la chaîne. Est-ce juste un std :: string, un tableau de caractères, ou un flux? Je suppose que vous utilisez des mutexes afin d'empêcher un autre thread d'afficher un message d'erreur courant pendant que vous écrivez un ancien dans le fichier correct? –

+0

Si vous lisez à partir d'un fichier, pouvez-vous garder une trace de combien de temps chaque ligne dans ce fichier est? std :: chaîne de caractères; getline (inputFile, ligne); int lengthInCharacters = line.length() –

4

Je ne suis pas sûr que je la situation, mais il semble que vous voulez un proxy:

class LogSingleton 
{ 
public: 
    LogSingleton& instance() { /* ... */ } 

    void lock(); // lock mutex 
    void unlock(); // unlock mutex 

    template <typename T> 
    friend LogSingleton& operator<<(LogSingleton& pLog, const T& pX) 
    { 
     // needs to be locked first 
     assert(is_locked()); 

     /* output pX however */ 

     return pLog; 
    } 
}; 

class LogProxy 
{ 
public: 
    LogProxy() 
    { 
     // manage lock in proxy 
     LogSingleton::instance().lock();    
    } 

    ~LogProxy() 
    { 
     LogSingleton::instance().unlock();    
    } 
}; 

// forward input into the proxy to the log, knowing it's locked 
template <typename T> 
LogProxy& operator<<(LogProxy& pProxy, const T& pX) 
{ 
    LogSingleton::instance() << pX; 

    return pProxy; 
} 

// now expose proxy 
typedef LogProxy log; 

Et vous feriez ceci:

log() << "its locked now" << "and the temporary will die" << "here ->"; 

Le verrouillage se fait en le constructeur et le destructeur, et le destructeur est appelé à la fin. Comme Tony le signale à juste titre, cela maintient le verrou inutilement long. Le verrou n'est nécessaire que pour la sortie "finale" du LogSingleton. Imaginez ceci:

log() << "this next function takes 5 minutes" 
     << my_utterly_crappy_function() << "ouch"; 

Nothings se connecté mais le mutex est verrouillé depuis longtemps.Il serait préférable de mémoire tampon de sortie à distance, puis sortie tout à la fois:

class LogProxy 
{ 
public: 
    ~LogProxy() 
    { 
     // manage lock in proxy 
     LogSingleton::instance().lock(); 

     // no-throw, or wrap mutex use in a scoped-lock 
     LogSingleton::instance() << mBuffer.rdbuf(); 

     LogSingleton::instance().unlock();    
    } 

    // buffer output 
    template <typename T> 
    friend LogProxy& operator<<(LogProxy& pProxy, const T& pX) 
    { 
     mBuffer << pX; 

     return pProxy; 
    } 

private: 
    std::ostringstream mBuffer; 
}; 

Maintenant, pas de serrures sont acquises jusqu'à ce que le tampon est prêt à émettre.

+0

Si l'application enregistre beaucoup de données, cette solution peut sérieusement compromettre les performances. Bien qu'il soit moins efficace pour la journalisation de la lumière ou lorsqu'un thread effectue presque toute la journalisation, il est plus évolutif de concaténer la sortie dans un ostringstream, puis de prendre le verrou de singleton uniquement lorsque vous êtes prêt à écrire. (Sur certains systèmes, un seul write() vers un flux sera effectué alors que le flux/fichier contient son propre verrou mutex, ce qui garantit l'atomicité - mais ce n'est pas portable). –

+0

@Tony: Bon point. Je vais ajouter ça. – GManNickG