我同意上述观点,但在我的情况下,解决方案略有不同。
private static object locker = new object();
private static void WriteMessageToFile(string message)
{
string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
if (!Directory.Exists("Logs"))
{
DirectoryInfo di = Directory.CreateDirectory("Logs");
}
//Guid guidGenerator = Guid.NewGuid();
string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
FileInfo fi = new FileInfo(filePath);
lock (locker)
{
using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter streamWriter = new StreamWriter(file))
{
streamWriter.WriteLine(message);
streamWriter.Close();
}
}
}
因为下面这个函数在我的asp.net核心应用中很多地方都调用了异步和异步。在这种情况下,一个线程试图写入一个文件,另一个线程想要写入同一个文件,结果出现了错误。作为解决方案,我尝试了上述方法,但它也不起作用,因为我试图在关闭前一个流之前打开一个新流。所以我决定编写一个安全的代码块作为解决方案。在这种情况下,由于其他线程无法到达锁定区域,他们通过等待前面的操作进行写入操作,我能够正确写入文件。
我认为;背后还有另一个原因代码,因为我在启动时使用了单例注册。这个函数的调用者类是相互隔离的。由于这个原因,他们不知道之前调用了哪个线程的函数。他们的一生已经结束了。 FileStream 还包装了 StreamWriter ,那么它也可以在没有锁定的情况下工作,无论如何它是有保证的。
即使是 Microsoft.Extensions.Logging 默认也不支持 FileLoger,但我们可以编写自定义。我在下面分享整个实现
public class FileLoger : ILogger
{
public static IHostingEnvironment _env;
private static object locker = new object();
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var message = string.Format("{0}: {1} - {2}", logLevel.ToString(), eventId.Id, formatter(state, exception));
WriteMessageToFile(message);
}
private static void WriteMessageToFile(string message)
{
string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
if (!Directory.Exists("Logs"))
{
DirectoryInfo di = Directory.CreateDirectory("Logs");
}
//Guid guidGenerator = Guid.NewGuid();
string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
FileInfo fi = new FileInfo(filePath);
lock (locker)
{
using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter streamWriter = new StreamWriter(file))
{
streamWriter.WriteLine(message);
streamWriter.Close();
}
}
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
}
public class FileLogProvider : ILoggerProvider
{
public FileLogProvider(IHostingEnvironment env)
{
FileLoger._env = env;
}
public ILogger CreateLogger(string category)
{
return new FileLoger();
}
public void Dispose()
{
}
}