2012-09-11 68 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。或者如果您想实时在外部监控日志,则必须每次都调用它。

是的,关键部分会对性能产生影响,但与写入文件的性能成本相比,这并不算什么。无需担心,您可以每秒钟输入几千次关键部分。