Sea*_*man 50 c# logging multithreading thread-safety
以下是实现相当简单的线程安全日志记录类的正确方法吗?
我知道我从未明确关闭过TextWriter,这会是一个问题吗?
当我最初使用该TextWriter.Synchronized方法时,它似乎没有工作,直到我在静态构造函数中初始化它并使它只读它:
public static class Logger
{
static readonly TextWriter tw;
static Logger()
{
tw = TextWriter.Synchronized(File.AppendText(SPath() + "\\Log.txt"));
}
public static string SPath()
{
return ConfigManager.GetAppSetting("logPath");
}
public static void Write(string logMessage)
{
try
{
Log(logMessage, tw);
}
catch (IOException e)
{
tw.Close();
}
}
public static void Log(string logMessage, TextWriter w)
{
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Run Code Online (Sandbox Code Playgroud)
x0n*_*x0n 93
我将采取与其他答案完全不同的方法,并假设您实际上想要学习如何编写更好的线程感知代码,并且不寻找我们的第三方建议(即使您实际上可能最终使用一.)
正如其他人所说,你正在创建一个线程安全的TextWriter,这意味着对WriteLine的调用是线程安全的,这并不意味着一堆调用WriteLine将作为原子操作执行.我的意思是不能保证四个WriteLine调用将按顺序发生.你可能有一个线程安全的TextWriter,但你没有线程安全的Logger.Log方法;)为什么?因为在这四个调用期间的任何时候,另一个线程可能还决定调用Log.这意味着您的WriteLine通话将不同步.解决这个问题的方法是使用如下lock语句:
private static readonly object _syncObject = new object();
public static void Log(string logMessage, TextWriter w) {
// only one thread can own this lock, so other threads
// entering this method will wait here until lock is
// available.
lock(_syncObject) {
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Run Code Online (Sandbox Code Playgroud)
所以,现在你有一个线程安全的TextWriter和线程安全的Logger.
合理?
虽然调用TextWriter.Synchronized将保护该单个实例TextWriter,但它不会同步您的写入,以便一个"Log"调用在文件内保持在一起.
如果您从多个线程调用Write(或Log使用内部TextWriter实例),则各个WriteLine调用可能会交织在一起,使您的日期和时间戳无法使用.
我个人会使用已存在的第三方日志记录解决方案.如果这不是一个选项,那么自己同步(即使使用简单的锁定)可能比使用框架的TextWriter.Synchronized包装器更有用.
有人在今天讨论一些日志记录问题时向我指出了这篇文章。我们在这里已经有了很好的答案,但我添加我的答案只是为了展示一个更简单的Logger课程版本,它以完全相同的Threadsafe方式完成相同的事情。
这里要注意的一件主要事情是,TextWriter.Synchronized线程安全不需要,因为我们正在将文件写入适当的lock.
注意:这已经在 x0n 答案的评论部分中讨论过。
public static class Logger
{
static readonly object _locker = new object();
public static void Log(string logMessage)
{
try
{
var logFilePath = Path.Combine(@"C:\YourLogDirectoryHere", "Log.txt");
//Use this for daily log files : "Log" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
WriteToLog(logMessage, logFilePath);
}
catch (Exception e)
{
//log log-exception somewhere else if required!
}
}
static void WriteToLog(string logMessage, string logFilePath)
{
lock (_locker)
{
File.AppendAllText(logFilePath,
string.Format("Logged on: {1} at: {2}{0}Message: {3}{0}--------------------{0}",
Environment.NewLine, DateTime.Now.ToLongDateString(),
DateTime.Now.ToLongTimeString(), logMessage));
}
}
}
Run Code Online (Sandbox Code Playgroud)
要记录一些东西,只需调用
Logger.Log("Some important event has occurred!");
Run Code Online (Sandbox Code Playgroud)
它会做一个这样的日志条目
登录:2015 年 10 月 7 日 at:02
:11 :23消息:发生了一些重要事件!
--------------------