2010-12-15 81 views
2

創建我自己的std::cerr以便它是逐行線程安全的最簡單方法是什麼?C++的基於線程的線程安全的std :: cerr

我最好尋找代碼來做到這一點。

我需要的是當我在控制檯上看到它時,由一個線程生成的a line of output(以std :: endl結尾)保持as a line of output。 (並沒有與其他線程的輸出混合)

解決方案std::cerr比cstdio慢很多。我更喜歡在CriticalSectionLocker類中使用fprintf(stderr, "The message") whos的構造函數獲得線程安全鎖,並且desturctor釋放它。

+1

你如何想像創建線程本地緩衝區 「上鉤」 到'標準:: cerr'會減少線程本地緩衝區「外部」的緩衝區,然後將全部行寫入'std :: cerr'?緩衝區是一個緩衝區。 'std :: ostringstream'是一個典型的通用方法。 – 2010-12-15 03:46:49

+3

你有沒有機會尋找一個線程安全日誌庫? – yasouser 2010-12-15 03:47:27

+1

我最近了解到了log4cpp項目(http://log4cpp.sourceforge.net/)。不確定它是否提供你正在尋找的東西!?!?也許值得檢查一下。 – yasouser 2010-12-15 04:01:19

回答

3

此:在大多數編譯器

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

作品的myerr("ERR: " << message << number)常見的情況。

+0

這假定你不想使用'<<'操作符來格式化(例如'cerr <<「這個日誌消息已經被打印出來了<< << n次<< <<次。」<< endl;') – 2010-12-15 14:54:01

+2

Hrm。我其實是錯的,因爲我暫時忘記了宏是如何工作的。你需要做的所有事情都是調用'lerr(「這個日誌消息已經被打印出來了」<< n次<<「次)」,而文本替換就會正確。 – 2010-12-15 18:53:11

5

你沒有。沒有任何關於std::cerr(也不是整個語言中的任何其他共享對象)可以知道哪個線程正在對<< operator進行特定呼叫以防止交織。

你能做的最好的是創建一批線程特定的日誌對象,每個積累數據,直到達到一個換行符,然後收購,保護cerr以便同時保持一整行寫在一次cerr的鎖鎖。

+0

這樣經過良好測試的類庫要多得多,我打算使用自己的類。我如何編寫這些描述的基於iostream的記錄器對象?那是我原來的問題。我對cerr <<「message」 – unixman83 2010-12-15 04:13:42

+0

以外的iostreams一無所知。我不太確定。我認爲有以下幾種選擇:#1你可以編寫你自己的streambuf,將數據刷新到'cerr'並使用一個普通的ostream爲它格式化數據,#2你可以在每個線程的記錄器中使用一個'std :: stringstream'來累積數據(stringstream與所有'cerr'的格式化基元完全兼容,但是暴露它們更加有效)。在哪裏獲得鎖定以清除cerr將是平臺特定的。 #3你可以用'sprintf'做些什麼,然後格式化字符串。 – 2010-12-15 04:19:55

+0

或者像log4cpp – 2010-12-15 04:21:35

2

對unixman評論中的方法進行了改進(並不真正適合評論)。

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

如果ErrCriticalSectionLocker是認真落實其中可用於像

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

但是,我個人更喜歡肯的建議。

+0

+1用於創造性地使用臨時生命週期。你能否提供一個'ErrCriticalSectionLocker'的實現,因爲語義非常*非平凡? – 2010-12-15 14:55:44

+0

嗯。我懂了;你可以使用這個方法而不用***'括號'***,但是我沒有看到這一點,因爲我的C(我來自C背景)編程風格通常要求括號。另外,ErrCriticalSectionLocker令人困惑...... – unixman83 2011-07-18 09:00:39

4

這是一個基於線程安全行的日誌記錄解決方案,我在某個時候製作了。它使用boost mutex來保證線程安全。這是略超過必要的複雜,因爲你可以在輸出政策塞(它應該去的文件,標準錯誤,或別的地方?):

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 

用法如下:

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

注缺乏'\n'std::endl因爲它是隱含的。內容被緩衝,然後使用模板指定策略以原子方式輸出。這個實現也會在行的前面添加一個時間戳,因爲它用於記錄目的。 no_output策略非常可選,這是我在禁用日誌記錄時使用的策略。

3

爲什麼不只是創建一個鎖定類,並在任何想要執行線程安全IO的地方使用它?

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(); 

然後你把任何IO要在一個塊:

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

變爲:

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