如何优化在后台写入多个文件
How to optimize writing to multiple files in the background
我的程序需要经常将消息写入多个文件。由于它非常耗时,我需要对其进行优化。在下面,您可以找到我的程序的摘录,我在其中尝试在后台将异步写入文件。它似乎有效,但我不确定这是否是最佳实践,因为我不处理任务(这部分已评论)。我不这样做是因为我不想让我的程序等待那些任务完成。简单地说,我希望我的消息尽快写入后台的几个文件。由于这些文件可以被多个线程访问,我添加了锁。
我使用静态方法是因为这些方法在我的代码中无处不在,我不想实例化这个 class,只是将一行消息写入文件,无处不在(也许这是错误的)。
==================Class ====================== ========================
namespace test
{
public static class MessageLOG
{
private static string debugFileName = Settings.DebugLOGfilename;
private static string logFileName = Settings.LOGfilename;
private static object DebuglockOn = new object();
private static object LoglockOn = new object();
private static StreamWriter DebugSW;
private static StreamWriter LogSW;
private static void DebugFile(string message)
{
uint linesCount = 0;
string _filename = debugFileName;
if(DebugSW == null && !string.IsNullOrEmpty(_filename))
DebugSW = new StreamWriter(_filename);
if(DebugSW != null)
{
lock(DebuglockOn)
{
DebugSW.WriteLine(message);
linesCount++;
if (linesCount > 10)
{
DebugSW.Flush();
linesCount = 0;
}
}
}
}
private static void LogFile(string message)
{
uint linesCount = 0;
string _filename = logFileName;
if(LogSW == null && !string.IsNullOrEmpty(_filename))
LogSW = new StreamWriter(_filename);
if(LogSW != null)
{
lock(LoglockOn)
{
LogSW.WriteLine(string.Format("{0} ({1}): {2}", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), message));
linesCount++;
if (linesCount > 10)
{
LogSW.Flush();
linesCount = 0;
}
}
}
public static void LogUpdate(string message)
{
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => LogFile(message)));
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => DebugFile(message)));
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => Debug.WriteLine(message)));
}
//This method will be called when the main thread is being closed
public static void CloseAllStreams()
{
if (DebugSW != null)
{
DebugSW.Flush();
DebugSW.Close();
}
if (LogSW != null)
{
LogSW.Flush();
LogSW.Close();
}
}
=============== main window ===========
void MainWIndow()
{
... some code ....
MessageLog.LogUpdate("Message text");
... code cont ....
MessageLog.CloseAllStreams();
}
您正在打开一个新流并为每个写入操作提交写入:性能非常差。
我的建议是每个文件只使用一个StreamWriter
,这个实例必须是一个class字段并且你仍然需要使用锁来确保线程安全。
此外,这还要求您不要在每个写入方法中使用 using
语句。
也可以定期,也许每 X 次写入,您可以 Stream.Flush
将写入提交到磁盘上。这个同花顺必须用锁保护。
您应该重新考虑您的设计。你的锁不应该是你方法中的局部变量。这是多余的,因为每个方法调用都会创建一个新对象并锁定它。这不会强制跨多个线程 (https://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx). Since your methods are static, the locks need to be static variables and you should have a different lock per file. You can use ThreadPool.QueueUserWorkItem (https://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx) 而不是任务进行同步。 ThreadPool 是一个内部 .NET class,可将线程重新用于 运行 异步操作。这非常适合您的用例,因为您不需要控制每个线程。您只需要一些异步操作即可自行执行和完成。
更好的方法是创建一个 class 在其自己的线程上 运行 的记录器。您可以有一个队列,并使来自多个线程的消息入队,然后让 LoggerThread 处理写入文件的操作。这将确保只有一个线程在写入文件。如果您使用 FIFO 队列,这也将维护日志记录顺序。您将不再需要锁定对文件的写入,但您需要锁定您的队列。您可以使用 .NET Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) class 来阻止 LoggerThread,直到消息排队(查看方法 Enter/Wait/Pulse)。为了进一步优化它,您现在可以保持文件流打开,并在文件排队时将数据推送给它。因为只有一个线程曾经访问过该文件,所以这没问题。请记住在完成后关闭文件流。您还可以设置一个计时器,该计时器偶尔响起以刷新内容。并不总是建议保持流打开,尤其是当您预期其他应用程序试图锁定该文件时。但是,在这种情况下,它可能没问题。这将是您需要做出的最适合您的应用程序的设计决定。
我的程序需要经常将消息写入多个文件。由于它非常耗时,我需要对其进行优化。在下面,您可以找到我的程序的摘录,我在其中尝试在后台将异步写入文件。它似乎有效,但我不确定这是否是最佳实践,因为我不处理任务(这部分已评论)。我不这样做是因为我不想让我的程序等待那些任务完成。简单地说,我希望我的消息尽快写入后台的几个文件。由于这些文件可以被多个线程访问,我添加了锁。 我使用静态方法是因为这些方法在我的代码中无处不在,我不想实例化这个 class,只是将一行消息写入文件,无处不在(也许这是错误的)。
==================Class ====================== ========================
namespace test
{
public static class MessageLOG
{
private static string debugFileName = Settings.DebugLOGfilename;
private static string logFileName = Settings.LOGfilename;
private static object DebuglockOn = new object();
private static object LoglockOn = new object();
private static StreamWriter DebugSW;
private static StreamWriter LogSW;
private static void DebugFile(string message)
{
uint linesCount = 0;
string _filename = debugFileName;
if(DebugSW == null && !string.IsNullOrEmpty(_filename))
DebugSW = new StreamWriter(_filename);
if(DebugSW != null)
{
lock(DebuglockOn)
{
DebugSW.WriteLine(message);
linesCount++;
if (linesCount > 10)
{
DebugSW.Flush();
linesCount = 0;
}
}
}
}
private static void LogFile(string message)
{
uint linesCount = 0;
string _filename = logFileName;
if(LogSW == null && !string.IsNullOrEmpty(_filename))
LogSW = new StreamWriter(_filename);
if(LogSW != null)
{
lock(LoglockOn)
{
LogSW.WriteLine(string.Format("{0} ({1}): {2}", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), message));
linesCount++;
if (linesCount > 10)
{
LogSW.Flush();
linesCount = 0;
}
}
}
public static void LogUpdate(string message)
{
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => LogFile(message)));
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => DebugFile(message)));
ThreadPool.QueueUserWorkItem(new WaitCallback( (x) => Debug.WriteLine(message)));
}
//This method will be called when the main thread is being closed
public static void CloseAllStreams()
{
if (DebugSW != null)
{
DebugSW.Flush();
DebugSW.Close();
}
if (LogSW != null)
{
LogSW.Flush();
LogSW.Close();
}
}
=============== main window ===========
void MainWIndow()
{
... some code ....
MessageLog.LogUpdate("Message text");
... code cont ....
MessageLog.CloseAllStreams();
}
您正在打开一个新流并为每个写入操作提交写入:性能非常差。
我的建议是每个文件只使用一个StreamWriter
,这个实例必须是一个class字段并且你仍然需要使用锁来确保线程安全。
此外,这还要求您不要在每个写入方法中使用 using
语句。
也可以定期,也许每 X 次写入,您可以 Stream.Flush
将写入提交到磁盘上。这个同花顺必须用锁保护。
您应该重新考虑您的设计。你的锁不应该是你方法中的局部变量。这是多余的,因为每个方法调用都会创建一个新对象并锁定它。这不会强制跨多个线程 (https://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx). Since your methods are static, the locks need to be static variables and you should have a different lock per file. You can use ThreadPool.QueueUserWorkItem (https://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx) 而不是任务进行同步。 ThreadPool 是一个内部 .NET class,可将线程重新用于 运行 异步操作。这非常适合您的用例,因为您不需要控制每个线程。您只需要一些异步操作即可自行执行和完成。
更好的方法是创建一个 class 在其自己的线程上 运行 的记录器。您可以有一个队列,并使来自多个线程的消息入队,然后让 LoggerThread 处理写入文件的操作。这将确保只有一个线程在写入文件。如果您使用 FIFO 队列,这也将维护日志记录顺序。您将不再需要锁定对文件的写入,但您需要锁定您的队列。您可以使用 .NET Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) class 来阻止 LoggerThread,直到消息排队(查看方法 Enter/Wait/Pulse)。为了进一步优化它,您现在可以保持文件流打开,并在文件排队时将数据推送给它。因为只有一个线程曾经访问过该文件,所以这没问题。请记住在完成后关闭文件流。您还可以设置一个计时器,该计时器偶尔响起以刷新内容。并不总是建议保持流打开,尤其是当您预期其他应用程序试图锁定该文件时。但是,在这种情况下,它可能没问题。这将是您需要做出的最适合您的应用程序的设计决定。