2012-09-11 97 views
0

我創建一個多線程的程序和多個線程可能需要調用一個全局函數併發日誌文件訪問/ C++

writeLog(const char* pMsg); 

和WRITELOG將實施類似tihs:

void writeLog(const char* pMsg) 
{ 
    CRITICAL_SECTION cs; 

    // initialize critical section 
    ... 

    EnterCriticalSection(&cs); 

    // g_pLogFilePath is a global variable. 
    FILE *file; 
    if (0!=fopen_s(&file, g_pLogFilePath, "r+")) 
     return; 

    fprintf(file, pMsg); 

    fclose(file): 
    LeaveCriticalSection(&cs); 
} 

我的問題是:

1) is it the best way to do concurrent logging? i.e., using critical section. 

2) since I will write log in many places in the threads, 
and since each log writing will involve open/close file, 
does the io will impact the performance significantly? 

謝謝!

+2

如果'cs'是一個局部變量,那麼每次輸入'writeLog'(當你返回時都會泄漏它),你就會創建一個新的臨界區,所以它根本不是關鍵部分。 –

+0

臨界區對象必須是全局的,[見MSDN](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686908%28v=vs.85%29.aspx)。 –

回答

2

CS是一種合理的方法來保護日誌記錄,是的。爲避免在每個線程的每次調用時都造成打開/寫入/關閉,通常會將字符串排隊(如果尚未進行mableced/newed,則可能需要將其複製)到單獨的日誌線程。阻止磁盤延遲隨後從日誌記錄調用中緩衝。任何懶惰寫作等優化可以在日誌線程中實現。

另外,正如其他海報所建議的,只需使用一個已經實現所有這些東西的日誌框架。

4

併發日誌記錄的最好方法是使用existing log library for C++之一。 他們有很多你可能會喜歡使用的功能(不同的appender,格式化,併發等)。

如果你還是希望有自己的解決方案,你很可能有這樣的事情: 簡單單是初始化一次,並保持狀態(文件處理程序和互斥)

class Log 
{ 
public: 

    // Singleton 
    static Log & getLog() 
    { 
     static Log theLog; 
     return theLog; 
    } 

    void log(const std::string & message) 
    { 
     // synchronous writing here 
    } 
private: 
    // Hidden ctor 
    Log() 
    { 
     // open file ONCE here 
    } 

    // Synchronisation primitive - instance variable 
    // CRITICAL_SECTION or Boost mutex (preferable) 
    CRITICAL_SECTION cs_; 

    // File handler: FILE * or std::ofstream 
    FILE * handler_; 
}; 
+0

不要忘記在構造函數中初始化CriteriaSection =) – paddy

+0

@paddy:如果你更喜歡CRITICAL_SECTION - 那麼是的,當然。但是我仍然會推薦boost mutex(或者std ::如果你有現代編譯器)。初始化更簡單,並且日誌寫入將是異常安全的。 – nogard

3

回答您的問題:

  1. 是的,一個關鍵部分確實是需要併發日誌記錄。

  2. 是的,記錄可能的確會影響性能。

正如在評論中提到的,用來「保護」對象中的關鍵部分必須由所有線程訪問,如全局變量或單身。

關於測井性能,IO的成本很高。一種常見的方法是有一個記錄對象,用於緩存要記錄的消息,並且只在緩衝區滿時寫入。這將有助於表現。另外,考慮有幾個日誌級別:DEBUG,INFO,WARNING,ERROR。

1

我在寫一個答案,然後斷路器跳閘。由於我的答案仍在草稿中,我可能還會繼續。與提供單身人士課程的答案大致相同,但我更像C一樣。這全部在單獨的源文件中(例如,Logging.cpp)。

static CRITICAL_SECTION csLogMutex; 
static FILE *fpFile = NULL; 
static bool bInit = false; 

bool InitLog(const char *filename) 
{ 
    if(bInit) return false; 
    bInit = true; 
    fpFile = fopen(filename, "at"); 
    InitializeCriticalSection(&csLogMutex); 
    return fpFile != NULL; 
} 

void ShutdownLog() 
{ 
    if(!bInit) return; 
    if(fpFile) fclose(fpFile); 
    DeleteCriticalSection(&csLogMutex); 
    fpFile = NULL; 
    bInit = false; 
} 

那些被稱爲應用程序中的入口/出口。至於記錄,我更喜歡使用變量參數列表,所以我可以做printf風格的記錄。

void writeLog(const char* pMsg, ...) 
{ 
    if(fpFile == NULL) return; 

    EnterCriticalSection(&csLogMutex); 

    // You can write a timestamp into the file here if you like. 

    va_list ap; 
    va_start(ap, pMsg); 
    vfprintf(fpFile, pMsg, ap); 
    fprintf(fpFile, "\n");  // I hate supplying newlines to log functions! 
    va_end(ap); 

    LeaveCriticalSection(&csLogMutex); 
} 

如果您打算在DLL中進行日誌記錄,則不能使用此靜態方法。相反,您需要使用_fsopen打開文件並拒絕讀/寫共享。

如果您希望應用程序崩潰,您也可以定期致電fflush。或者如果您想實時在外部監控日誌,則必須每次都調用它。

是的,關鍵部分會對性能產生影響,但與寫入文件的性能成本相比,這並不算什麼。無需擔心,您可以每秒鐘輸入幾千次關鍵部分。