我正在创建一个多线程程序,并且几个线程可能需要调用全局函数
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?
谢谢!
答案 0 :(得分: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_;
};
答案 1 :(得分:3)
回答你的问题:
是的,并发日志确实需要一个关键部分。
是的,记录可能确实会影响性能。
正如评论中所提到的,用于“保护”关键部分的对象必须可被所有线程访问,例如全局变量或单例。
关于日志记录性能,IO可能代价高昂。一种常见的方法是使用一个日志记录对象来缓冲要记录的消息,并且只在缓冲区已满时写入。这将有助于提高性能。另外,考虑有几个日志级别:DEBUG,INFO,WARNING,ERROR。
答案 2 :(得分:2)
CS是保护日志记录的合理方法,是的。为了避免在每个线程的每次调用时造成打开/写入/关闭,通常将字符串排队(如果尚未进行malloced / newed,则可能需要将其复制)到单独的日志线程。然后从日志记录调用缓冲阻止磁盘延迟。任何延迟写入等优化都可以在日志线程中实现。
或者,正如其他海报所建议的那样,只需使用已经实现了所有这些内容的日志框架。
答案 3 :(得分: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
。或者,如果您想要实时外部监控日志,则必须每次都调用它。
是的,关键部分会对性能产生影响,但与写入文件的性能成本无关。您可以毫不费力地每秒输入关键部分数千次。