2013-03-09 2 views
0

Я пытаюсь создать поточно-безопасный метод.Любые проблемы параллелизма с моим кодом

Кто-нибудь видит проблемы параллелизма со следующим кодом?

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

//static locker for thread synchronization 
    private static readonly System.Object _object3 = new System.Object(); 

    //this method needs to be thread-safe 
    public static void LogToTextFile(string logMessage, LogLevel logType) 
    { 
     //make sure only one thread executes this file writing code at a time    
     lock (_object3) 
     { 
      using (StreamWriter w = File.AppendText(@"c:\logs\log1.txt"); 
      { 
       w.WriteLine("\r\n{0} logged at {1} {2} : {3}", logType.ToString().ToUpper(), DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString(), logMessage); 
      } 
     } 
    } 
+1

Если у вас все в порядке с высокой степенью сериализации, это должно работать нормально.(Это скорее общий комментарий к случаю, вы все равно не можете сериализовать ввод-вывод.) Проблемы параллелизма, как правило, возникают из-за координации доступа к нескольким «ресурсам» или совместного использования ресурсов, что не является вашим делом , – millimoose

+1

Ой, очевидно, что проблема может возникнуть, если какой-либо другой код попытается открыть тот же файл. – millimoose

+0

@millimose - вы имеете в виду, что другая часть кода записывает в тот же файл или читает из того же файла. Я думаю, что чтение не вызовет проблем, но письмо будет? – Sunil

ответ

3

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

+0

Спасибо. Моя основная проблема заключалась в том, что я не мог ее протестировать. Я думаю, что очень сложно, если не невозможно, имитировать одновременный доступ к методу в VS 2010? – Sunil

+0

@Sunil Как так? Просто создайте кучу потоков, которые его называют. Проблема возникает при моделировании неправильного типа одновременного доступа, поскольку у вас нет почти необходимого контроля над планированием. – millimoose

+2

Есть инструменты, которые систематически анализируют ваш код. Одним из них является [шахматы] (http://research.microsoft.com/en-us/projects/chess/) – flup

1

Учитывая, что у вас есть lock вокруг объекта с соответствующей областью, я не вижу проблем с параллелизмом с вашим кодом.

Однако, как упоминалось в комментариях к миллионусу, у вас есть потенциальная проблема с производительностью. Если два потока попытаются одновременно установить LogToTextFile, один из них сначала получит блокировку, а другой будет блокироваться, пока файл будет открыт, записан и закрыт. Это может быть неприемлемым, в зависимости от того, что делают ваши потоки.

Если я упоминаю проблему, вам придется реализовать более сложный потокобезопасный регистратор. Как правило, будет очередь, в которой записываются события журнала. Затем есть еще один поток, который периодически просыпается и опустошает очередь на файл на диске. Все операции в очереди выполняются с помощью блокировки.

+0

Проблема * * может быть решена путем сбора журнальных сообщений в очереди, а затем с использованием еще одного дескриптора ввода-вывода, но это поможет только в том случае, если сообщения журнала попадают в пакеты. И, скорее всего, приведет к более сложным проблемам синхронизации. – millimoose

+1

Согласовано, это не тривиальная задача для реализации такого регистратора-читателя-писателя. Однако, не зная, что делает его приложение, я не могу сказать, будет ли подход блокировки приемлемым или нет. –

+0

@ Джонатан - Разве это не компромисс, если вы хотите сделать метод потокобезопасным? – Sunil

1

Другим, возможно, лучшим решением может стать оставить файл открытым и использовать синхронизированный TextWriter для выполнения операций записи. TextWriter.Synchronized обеспечивает поточно-обертку вокруг TextWriter:

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

Вот простая реализация. Обратите внимание, что это не было протестировано.

public static class MyLogger 
{ 
    private static TextWriter s_writer; 

    // Not thread-safe. Call this before other threads are allowed to call Log. 
    public void Open(string filename) { 
     s_writer = TextWriter.Synchronized(File.AppendText(filename)); 
    } 

    // Also not thread-safe. 
    public void Close() { 
     s_writer.Dispose(); 
    } 

    // Thread-safe. 
    public void Log(string logMessage, LogLevel logType) { 
     s_writer.WriteLine("\r\n{0} logged at {1} {2} : {3}", 
      logType.ToString().ToUpper(), DateTime.Now.ToLongTimeString(), 
      DateTime.Now.ToLongDateString(), logMessage); 
    }  
} 
+0

@Jonatah - Единственная проблема с созданием статического TextWriter - это если возникает непредвиденная ошибка, то как мы будем гарантировать, что метод Close() определенно называется ? – Sunil

+0

Я полагаю, что другой вариант состоит в том, чтобы не использовать «Синхронизированный» TextWriter, а вместо этого «блокировать» объект TextWriter и улавливать любые исключения, вызывая 'Close()' на писателе. Опять же, я просто пытаюсь дать OP какое-то направление, а не обеспечивать для него поточную реализацию регистратора. –

1

В этой конкретной функции отсутствуют проблемы параллелизма. Оператор lock будет правильно умерить доступ ко внутреннему коду.

Однако по-прежнему существуют проблемы с параллелизмом при доступе к @"c:\logs\log1.txt". Возможно, что другая часть вашего приложения или другой процесс на том же компьютере попытается записать файл в то же время, что и ваш код. Следовательно, возможно, код StreamWriter не удастся даже с этой блокировкой.

Невозможно обойти этот тип проблем параллелизма. Файловая система - это общий ресурс, который у вас нет прав для защиты. Вместо этого ваш код должен принять, что доступ к файловой системе может завершиться неудачно и должным образом обработать это следствие.

Как правило, при работе с функциями регистрации я обертываю доступ к файловой системе с помощью try/catch и проглатываю все возникающие исключения. В противном случае вы рискуете от сбоя функции регистрации, сняв приложение. Это может быть или не быть приемлемым для вашего сценария.

+0

Отличный совет. Я ценю. – Sunil

+0

Благодаря вашему совету, я изменил метод LogToTextFile, поэтому теперь он пытается записать в файл снова для максимум 5 попыток, в случае ошибки. Я пишу в файл с помощью try-catch. Я не делаю исключения в уловке. Делает мой код очень профессиональным. Надеюсь, я могу дать вам миллион очков. – Sunil

+0

Еще одна моя библиотека регистрации бросала исключение, если я открыл файл журнала в «Блокноте» и сохранил его, когда ведение журнала продолжалось. – Sunil

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