Блокировка Streamwriter не работает - PullRequest
4 голосов
/ 14 сентября 2011

Я беру проект C # и при тестировании получаю ошибки. Ошибка в том, что файл журнала не может быть записан, потому что он используется другим процессом. Вот код:

    public void WriteToLog(string msg)
    {
        if (!_LogExists) 
        {
            this.VerifyOrCreateLogFile();  // Creates log file if it does not already exist.
        }

        // do the actual writing on its own thread so execution control can immediately return to the calling routine.
        Thread t = new Thread(new ParameterizedThreadStart(WriteToLog));
        t.Start((object)msg);
    }

    private void WriteToLog(object msg)
    {
        lock (_LogLock)
        {
            string message = msg as string;
            using (StreamWriter sw = File.AppendText(LogFile))
            {
                sw.Write(message);
                sw.Close();
            }
        }
    }

_LogLock определяется как переменная класса:

    private object _LogLock = 0;

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

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

Спасибо за любую помощь!

EDIT

Спасибо за быстрые ответы! Код для VerifyOrCreateLogFile () действительно использует _LogLock, так что это не должно быть проблемой. Он делает некоторую запись в журнал до того, как произойдет ошибка, поэтому он прекрасно справляется с созданием файла.

Проблема, по-видимому, в том, что раньше только один класс создавал экземпляр класса журнала, а теперь я добавил экземпляры в другие классы. Имеет смысл, что это создаст проблемы. Изменение поля _LogLock на статическое устраняет проблему.

Еще раз спасибо!

Ответы [ 4 ]

7 голосов
/ 14 сентября 2011

Блокировка не должна допускать попытки другого потока выполнить запись в файл журнала.

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

Если каждый (или даже некоторые) запросы журнала используют отдельный экземпляр, блокировка не защитит вас.

Вы можете легко «исправить» это, введя в поле _LogLock static:

private static object _LogLock = 0;

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

2 голосов
/ 14 сентября 2011

Я вижу 2 проблемы с кодом:

  • Блокировка должна быть одинаковой для всех «пользователей» этого класса Log, самое простое решение - создать либо _LogLock, либо полный класс static
  • VerifyOrCreateLogFile может создать проблему, если 2 или более параллельных потока вызывают WriteToLog, когда _LogExists ложно ...
1 голос
/ 14 сентября 2011

Одна из возможностей заключается в том, что ОС не снимает блокировку файла достаточно быстро, прежде чем вы выйдете из lock в WriteToLog, а другой поток, который был заблокирован в ожидании блокировки, попытался открыть его, прежде чем ОС закончила выпускать файл замок. Да, это может случиться. Вам либо нужно немного поспать, прежде чем пытаться открыть файл, централизовать запись в журнал для выделенного объекта (чтобы у него и только у него был доступ к этому файлу, и вам не нужно было беспокоиться о спорах о блокировке файлов) .

Другая возможность заключается в том, что вам нужно заблокировать

if (!_LogExists)  {
    this.VerifyOrCreateLogFile();  // Creates log file if it does not already exist.
}

Третья возможность состоит в том, что у вас есть несколько экземпляров любого класса, где размещаются эти методы. Объект блокировки не будет разделен между экземплярами (для его решения сделайте это static).

В конце концов, если вы не являетесь экспертом в написании безопасного многопоточного кода, просто позвольте кому-нибудь еще позаботиться об этом за вас. Используйте платформу, которая обрабатывает эти проблемы для вас (log4net?).

0 голосов
/ 14 сентября 2011

Вы можете сделать исполняемый код простым

удалением sw.Close () ;из вашего кода ...

сделай это .... будет работать нормально .....

...