Как предотвратить вызов метода класса до того, как класс будет готов? - PullRequest
1 голос
/ 21 июня 2010

Все еще изучаю C # ... так со мной, пожалуйста.

Вот мой класс "logger", который помогает мне создавать файлы журналов в моем проекте.

namespace MyProject
{
class Logger
{
    private FileInfo fFile;
    private DirectoryInfo dDir;

    /// <summary>Add a new entry to the log file.</summary>
    /// <param name="sData">The line to add.</param>
    public void Add(string sData)
    {
        DateTime CurrTime = DateTime.Now;

        if (fFile.Length > 1048576)
        {
            fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
            fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));
            using (StreamWriter sw = fFile.CreateText())
            {
                sw.WriteLine("{0:u}|{1}", CurrTime, sData);
            }
        }
        else
        {
            using (StreamWriter sw = fFile.AppendText())
            {
                sw.WriteLine("{0:u}|{1}", CurrTime, sData);
            }
        }
    }

    /// <summary>Logger instance</summary>
    /// <param name="sFile">Full name of the file to use as logs. Ex : "MyLogs.txt"</param>
    public Logger(string sFile)
    {
        dDir = new DirectoryInfo(Path.Combine(MyProject.AppPath, "logs"));
        if (!dDir.Exists)
        {
            dDir.Create();
        }

        fFile = new FileInfo(Path.Combine(dDir.FullName,sFile));

        if (!fFile.Exists)
        {
            using (StreamWriter sw = fFile.CreateText())
            {
                sw.WriteLine("{0:u}|Logger Started", DateTime.Now);
            }
        }
        else
        {
            Add("Logger Started");
        }           
    }
}
}

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

Вместо того, чтобы просто убедиться, что logger.add не вызывается перед созданием файла, есть ли способ "заблокировать" класс?

Я пробовал метод блокировки, но он нене работает ... Lock (this) ничего не делал, и я не могу использовать его для самого метода.

Ответы [ 9 ]

2 голосов
/ 21 июня 2010

Проблема в том, что операция ввода-вывода кэширована.Теоретически это не должно быть проблемой, но практически это так.

Вы можете вызвать sw.Flush () в своем конструкторе.Это вынудит файл из кэша на диск и, следовательно, создать файл.

if (!fFile.Exists) 
{ 
    using (StreamWriter sw = fFile.CreateText()) 
    { 
        sw.WriteLine("{0:u}|Logger Started", DateTime.Now); 
        sw.Flush();
    } 
}
2 голосов
/ 22 июня 2010

Исключение на самом деле вызвано не тем, что файл не существует, а тем, что экземпляр FileInfo устарел! Вы создали FileInfo, когда файл не существует, и он делает снимок состояния файла в это время. Когда я проверяю это, исключение выдается, когда вы вызываете fFile.Length в методе Add. Если я добавлю в fFile.Refresh() вызов, я обнаружу, что он работает:

...
DateTime currTime = DateTime.Now;
fFile.Refresh();
if (fFile.Length > 1048576)
...

Смотрите здесь:

http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh.aspx

«Прежде чем пытаться получить информацию об атрибутах, необходимо выполнить вызовы Refresh, иначе эта информация будет устаревшей.»

1 голос
/ 21 июня 2010

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

    fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
    fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));

Это похоже на состояние гонки и должно быть сериализовано. У нас были одни проблемы даже после использования блокировок (perl / log4perl), и в итоге мы выбрали отдельные файлы журналов для каждого из процессов. Если не существует нескольких живых объектов Logger (даже между процессами, так как мы имеем дело с файлами), это не должно быть проблемой.

Чудеса, нет ли в C # эквивалента log4.

1 голос
/ 21 июня 2010

Вы хотите запретить запуск метода add до создания файла - вы можете сделать это, используя EventWaitHandle или, возможно, какой-то другой механизм управления потоками (возможно, используя класс монитора).

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

EventWaitHandle _handle = new EventWaitHandle (false, EventResetMode.ManualReset);
Object _fileLock = new Object();


 public Logger(string sFile)
{

// Do as you do here

_handle.Set();
}


public void Add(string sData)
{
    _handle.WaitOne ();    // Blocks the thread untill Set() is called on _handle


    Lock(_fileLock){    // Only one thread may enter at a time
       // Do as you do already
    }

}

Если подумать, использование Lock(_fileLock){ вокруг кода ctor должно выполнять ту же работу, что и WaitHandle.

1 голос
/ 21 июня 2010

В вашем конструкторе вы устанавливаете поля fFile и dDir, вместо этого у вас должно быть поле, являющееся потоковой записью, и используйте THAT в методе Add.

1 голос
/ 21 июня 2010

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

1 голос
/ 21 июня 2010

В методе проверьте, готов ли класс (что бы это ни было), и если он не ждет, верните ошибку или все, что вам нужно сделать.

1 голос
/ 21 июня 2010

Лично я бы не пошел по этому пути с блокировкой класса.Я бы взял код, который создает файл журнала и сделал его своим собственным методом.Затем, когда вызывается метод add, добавьте некоторую логику, чтобы проверить, существует ли файл журнала (по моему мнению, эта логика также должна быть собственным методом).Если файл существует, продолжайте ведение журнала, если нет, то создайте файл журнала (используя метод, который вы извлекли выше), а затем, когда этот метод успешно завершится, продолжайте и запишите свой журнал.

0 голосов
/ 21 июня 2010

EDIT - Я написал это на машине с Linux, когда у меня не было компилятора C #.Протестировав код на компьютере с Windows, я обнаружил, что проблема, обнаруженная здесь, на самом деле намного проще, чем мои предположения.Смотрите мой другой ответ.

Вы используете несколько тем здесь?Если да, то вам нужно будет использовать другой подход.Например, вы будете страдать от состояния гонки, если два потока попытаются вызвать Add одновременно и один или оба попытаются переместить файл.

Может ли любой другой процесс переместить или заблокировать ваш файл журнала, пока ваша программаработает, и если они это сделают, ваша программа не должна аварийно завершить работу?Если это так, вам нужно будет многое изменить, так как все ваши операции ввода-вывода могут быть неудачными.

Если , то ни один из этих случаев не применим, тогда онПохоже, ваша проблема в том, что файл хранится в некоторой файловой системе, которая демонстрирует некоторую задержку между созданием файла и возможностью его просмотра.Я не думал, что это имело место с NTFS или FAT32.Вы храните файл журнала на сетевом ресурсе?Не зная больше, я не могу придумать ничего лучшего, чем использовать предложение Rockinthesixstring о тестировании, если файл существует, и если не использовать короткий вызов Sleep (), пока он не появится (или пока вы не подождете достаточно долго, чтобы что-то прояснилось)пошло не так - может быть, кто-то удалил файл из-под вас).

Обратите внимание, что если проблема связана с файловой системой , никакая блокировка в классе не поможет,и не будет вызывать Flush, потому что оператор «using» гарантирует, что StreamWriter будет удален, что закроет файл в дополнение к тому же эффекту, что и Flush, поэтому Flush является избыточным.

Все, что сказано,почему вы закрываете файл и открываете его снова все время?Чтобы убедиться, что ничего не пропало в случае сбоя вашей программы?Если это так, то вам, вероятно, лучше следовать совету jmoreno, чтобы оставить TextWriter открытым, но вызывать Flush для него после того, как вы напишите каждую строку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...