2015-08-13 6 views
0

Моя программа должна часто писать сообщения нескольким файлам. Поскольку это очень много времени, мне нужно его оптимизировать. Ниже вы можете найти извлечение из моей программы, где я пытаюсь записать async в файл в фоновом режиме. Кажется, что это работает, но я не уверен, что это лучшая практика, поскольку я не распоряжаюсь задачами (эта часть прокомментирована). Я не делаю этого, потому что не хочу, чтобы моя программа дождалась завершения этих задач. Просто я хочу, чтобы мое сообщение было написано на несколько файлов в фоновом режиме как можно быстрее. Поскольку к этим файлам можно было обращаться несколькими потоками, я добавил блокировку. Я использую статические методы, потому что эти методы используются везде в моем коде, и я не хочу создавать экземпляр этого класса, просто чтобы написать одну строку сообщения в файл везде (возможно, это неправильно).Как оптимизировать запись в несколько файлов в фоновом режиме

================= Класс =========================================================== ===================

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

Может быть, немного оффтопик, и вы не отвечаете на свой вопрос, но вы понимаете, что есть много хороших и бесплатных фреймворков loggin? –

+0

Я только что начал свое путешествие с C#, но я использовал коды программ 15 лет назад. Теперь я борюсь с новыми событиями, такими как async, многопоточность, WPF. Я не знаю других фреймворков. Можете ли вы предложить, пожалуйста? – Zibi

+0

log4net - отличная структура ведения журнала.Вы можете вытащить его через менеджер пакетов Nuget в VS. – Tom

ответ

0

You're открытие нового потока и совершение письмен для каждого действия записи: очень плохого производительности.

Моя рекомендация состоит в том, чтобы использовать только один файл StreamWriter для каждого файла, этот экземпляр должен быть полем класса, и вам необходимо использовать блокировку для обеспечения безопасности потоков.

Также для этого потребуется, чтобы вы не использовали оператор using в каждом методе записи.

Также периодически, возможно, каждый X-номер записи, вы можете сделать Stream.Flush для фиксации записей на диске. Этот флеш должен быть защищен замком.

+0

Имеет смысл. Спасибо вам за ваши предложения. – Zibi

+0

JuanK. Я попытался отразить ваши предложенные изменения. Не могли бы вы посоветовать, нормально ли сейчас? Кажется, он работает очень хорошо. – Zibi

+0

Выглядит неплохо. Также вы можете улучшить его, проверив, что поток не закрыт до вызова метода записи, если он закрыт, поток должен быть повторно создан. linesCount должен быть полем класса или полем метода, и из-за этого вам нужны 2 поля «linesCount», по одному для каждого потока. Также я считаю, что счетчик может быть лучше, если вы используете константу для установки максимальной записи. – JuanK

1

Вы должны подумать о своем дизайне. Ваши блокировки не должны быть локальными переменными в вашем методе. Это избыточно, потому что каждый вызов метода создает новый объект и блокирует его. Это не приведет к синхронизации синхронизации по нескольким потокам (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) вместо задач. ThreadPool - это внутренний класс .NET, который повторно использует потоки для выполнения асинхронных операций. Это идеально подходит для вашего случая использования, потому что вам не нужен контроль над каждым потоком. Вам просто нужна операция async для выполнения и завершения самостоятельно.

Лучшим подходом было бы создать класс регистратора, который запускается в своем потоке. Вы можете иметь очереди и помещать в очередь сообщения из нескольких потоков, а затем записывать дескриптор LoggerThread в файл. Это обеспечит запись только одного потока в файл. Это также будет поддерживать порядок ведения журнала, если вы используете очередь FIFO. Вам больше не нужно будет блокировать запись в файл, но вам нужно будет заблокировать вашу очередь. Вы можете использовать класс .NET Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) для блокировки LoggerThread до тех пор, пока сообщение не будет поставлено в очередь (посмотрите на методы Enter/Wait/Pulse). Чтобы еще больше оптимизировать его, теперь вы можете держать поток открытым для файла и нажимать на него данные, поскольку он попадает в очередь. Поскольку только один поток когда-либо обращается к файлу, это будет ОК. Не забудьте закрыть поток в файл, когда закончите. Вы также можете настроить таймер, который отключается один раз в то время, чтобы очистить содержимое. Не рекомендуется поддерживать открытый поток, особенно если вы ожидаете, что другие приложения попытаются заблокировать файл. Однако в этом случае это может быть ОК. Это будет дизайнерское решение, которое вам нужно сделать, чтобы оно соответствовало вашему приложению.

+0

Полностью согласен с локальными переменными lockOn. Кроме того, я бы добавил, что вам нужны 2 разных объекта, таких как 'lockForDebugFunction' и' lockForLogFunction', потому что нет необходимости запрещать их одновременное выполнение. – Disappointed

+0

Чтобы подробнее описать комментарий @ tom-a о очереди, найдите пример модель «производитель потребительской нити». Если вы используете .NET4 или выше, вы можете использовать ConcurrentQueue (System.Collections.Concurrent.ConcurrentQueue <>), чтобы получить потокобезопасную очередь. –

+0

Вы можете использовать ConcurrentQueue, но вы все равно хотите заблокировать поток, пока работа не выполняется. Вы должны стараться избегать использования Thread.Sleep (int). Вы можете использовать AutoResetEvent/ManualResetEvent для блокировки или Монитор для блокировки и синхронизации. – Tom

Смежные вопросы