如何在后台优化对多个文件的写入

本文关键字:文件 后台 优化 | 更新日期: 2023-09-27 18:07:51

我的程序需要经常将消息写入几个文件。因为它非常耗时,我需要优化它。下面,你可以从我的程序中找到一个摘录,我试图在后台异步写入文件。它似乎有效,但我不确定它是否是最佳实践,因为我不处理任务(这部分是评论)。我不这样做是因为我不想让我的程序等待这些任务完成。简单地说,我希望我的消息尽可能快地写入后台的几个文件。由于这些文件可以被多个线程访问,所以我添加了锁。我使用静态方法是因为这些方法在我的代码中无处不在,我不想实例化这个类,只是为了在文件中到处写一行消息(也许这是错误的)。

================== 类 ==============================================

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(); 
}

如何在后台优化对多个文件的写入

你应该重新考虑你的设计。锁不应该是方法中的局部变量。这是多余的,因为每个方法调用都会创建一个新对象并锁定它。这不会强制跨多个线程进行同步(https://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx)。由于方法是静态的,锁需要是静态变量,并且每个文件应该有不同的锁。你可以使用ThreadPool。QueueUserWorkItem (https://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx)而不是Tasks。ThreadPool是一个内部。net类,它重用线程来运行异步操作。这对于您的用例来说是完美的,因为您不需要控制每个线程。你只需要一些异步操作自己执行和完成。

一个更好的方法是创建一个在自己的线程上运行的日志记录器类。您可以对来自多个线程的消息进行队列和排队,然后让LoggerThread处理对文件的写入。这将确保只有一个线程写入文件。如果您使用FIFO队列,这也将维持日志顺序。您将不再需要锁定对文件的写入,但您将需要锁定您的队列。您可以使用。net Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx)类来阻塞LoggerThread,直到消息排队(查看方法Enter/Wait/Pulse)。为了进一步优化它,现在可以对文件保持一个打开的流,并在它进入队列时向它推送数据。因为只有一个线程访问文件,所以这是可以的。只要记住在完成后关闭该文件的流。您还可以设置一个计时器,每隔一段时间关闭一次以刷新内容。并不总是建议保持流打开,特别是如果您预计其他应用程序试图锁定该文件。然而,在这种情况下,它可能是好的。这将是您需要做出的最适合您的应用程序的设计决策。

您正在打开一个新的流并为每个写操作提交写:性能非常差

我的建议是每个文件只使用一个StreamWriter,这个实例必须是一个类字段,你需要仍然使用锁来确保线程安全。

这也要求你不要在每个写方法中使用using语句。

也可以周期性地,也许每X次写操作,您可以创建一个Stream.Flush来提交磁盘上的写操作。此Flush必须由锁保护。