Проверьте, не используется ли файл другими экземплярами исполняемого файла

Прежде чем вдаваться в подробности, скажу, что моя программа написана в Visual Studio 2010 с использованием C# .Net 4.0.

Я написал программу, которая будет генерировать отдельные файлы журнала для каждого запуска. Файл журнала называется по времени и имеет точность до миллисекунды (например, 20130726103042375.log). Программа также создаст основной файл журнала за день, если он еще не существует (например, *20130726_Master.log*).

В конце каждого запуска я хочу добавить файл журнала в главный файл журнала. Есть ли способ проверить, могу ли я успешно добавить? И повторить попытку через Sleep примерно через секунду или около того?

По сути, у меня есть 1 исполняемый файл и несколько пользователей (скажем, 5 пользователей).

Все 5 пользователей будут получать доступ и запускать этот исполняемый файл одновременно. Поскольку практически невозможно, чтобы все пользователи запускались одновременно (с точностью до миллисекунды), не будет проблем с созданием отдельных файлов журналов.

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

Я использую метод

File.AppendAllText(masterLogFile, File.ReadAllText(individualLogFile));

У меня есть проверка объекта lock, но я думаю, что это не работает в моем случае, так как вместо нескольких потоков в одном экземпляре выполняется несколько экземпляров.

Еще один способ, который я рассматриваю, это try/catch, что-то вроде этого

try
{
    stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch {}

Но я не думаю, что это решит проблему, потому что статус masterLogFile может измениться за эту короткую миллисекунду.

Итак, мой общий вопрос: есть ли способ добавить в masterLogFile, если он не используется, и повторить попытку после короткого тайм-аута, если это так? Или если есть альтернативный способ создать masterLogFile?

Заранее спасибо и извините за длинное сообщение. Я хочу убедиться, что я донес свое сообщение и объяснить, что я пытался или исследовал, чтобы мы не тратили ничье время.

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


person sora0419    schedule 26.07.2013    source источник
comment
это может быть полезно: stackoverflow. ком/вопросы/50744/   -  person Alden    schedule 26.07.2013
comment
этот предыдущий пост также может помочь stackoverflow.com/questions/10210264/   -  person MethodMan    schedule 26.07.2013


Ответы (2)


Ваша попытка/поймать - это способ делать что-то. Если вызов File.Open завершится успешно, вы сможете записать в файл. Идея состоит в том, чтобы держать файл открытым. Я бы предложил что-то вроде:

bool openSuccessful = false;
while (!openSuccessful)
{
    try
    {
        using (var writer = new StreamWriter(masterlog, true)) // append
        {
            // successfully opened file
            openSuccessful = true;
            try
            {
                foreach (var line in File.ReadLines(individualLogFile))
                {
                    writer.WriteLine(line);
                }
            }
            catch (exceptions that occur while writing)
            {
                // something unexpected happened.
                // handle the error and exit the loop.
                break;
            }
        }
    }
    catch (exceptions that occur when trying to open the file)
    {
        // couldn't open the file.
        // If the exception is because it's opened in another process,
        // then delay and retry.
        // Otherwise exit.
        Sleep(1000);
    }
}
if (!openSuccessful)
{
    // notify of error
}

Поэтому, если вам не удается открыть файл, вы ложитесь спать и пытаетесь снова.

См. мой пост в блоге, File.Exists — это только снимок, чтобы получить более подробную информацию.

person Jim Mischel    schedule 26.07.2013
comment
Если я помещу весь try/catch в цикл while, прервусь, когда достигну конца внешней попытки, и засну во внешнем улове, то я получу ожидаемый результат? Я поместил метод в код режима, и он не дает ошибки компиляции, но, как я уже сказал, это редко случается, просто боюсь, что он сломается, пока я не знаю. - person sora0419; 26.07.2013
comment
@sora0419: см. мой обновленный пример кода. Ключом будет правильная обработка исключений. - person Jim Mischel; 26.07.2013
comment
Это решение является плохим дизайном. По крайней мере, добавьте конкретное исключение, которое он должен искать, когда дело доходит до блокировки файлов. Файл может даже не существовать, или в доступе может быть отказано и т. д. НО этот код будет держать вас на бесконечных, слепых, неэффективных американских горках. - person user1132959; 26.07.2013
comment
@ user1132959, см. комментарий в обработчике исключений. Я особенно рекомендую проверять тип исключения и действовать соответственно. - person Jim Mischel; 27.07.2013
comment
@JimMischel Пожалуйста, укажите эти исключения. Ответ неполный. - person user1132959; 28.07.2013
comment
@user1132959 user1132959, в документации описано, какие исключения конструктор должен вызывать и при каких обстоятельствах. Мне кажется излишним перечислять их здесь. - person Jim Mischel; 28.07.2013

Я бы сделал что-то вроде это, поскольку я думаю, что это требует наименьших накладных расходов. Try/catch будет генерировать трассировку стека (что может занять целую секунду), если возникнет исключение. Должен быть лучший способ сделать это атомарно. Если найду, выложу.

person user1132959    schedule 26.07.2013
comment
Во-первых, в данном контексте целая секунда — это вообще не время. Он читает и пишет большие файлы. Другой процесс, вероятно, будет открывать этот файл гораздо дольше, чем целую секунду, поэтому беспокоиться об этом времени просто смешно. Во-вторых, техника в ссылке идиотская, потому что он просто заменяет простой и эффективный обработчик исключений замысловатым кодом, который выполняет конечную работу вокруг среды выполнения .NET и не дает никаких особых преимуществ в этом процессе. - person Jim Mischel; 26.07.2013
comment
@JimMischel Целая секунда - это много времени для того, что должно занять несколько миллисекунд. Ваша попытка/поймать никогда не будет масштабироваться. Если 1000 процессов попытаются получить к нему доступ, ваша производительность будет снижена. Так что беспокоиться об этом времени не смешно, это просто не невежество. Просто окружать его обработчиком исключений — глупо и глупо. Вся цель состоит в том, чтобы дождаться эксклюзивного доступа, так почему бы не сделать это эффективно и правильно? Проверка на наличие ошибки до того, как вы позволите ей произойти, намного лучше, чем просто отлов ошибки и ее игнорирование (что занимает секунду). - person user1132959; 26.07.2013
comment
Если у вас есть 1000 или даже 100 процессов, конкурирующих за эксклюзивную блокировку файла, у вас гораздо большие проблемы. Ни одно решение, зависящее от монопольного доступа, не масштабируется. И даже связанное решение сталкивается с ошибкой. Он просто обрабатывает ошибку по-другому. - person Jim Mischel; 27.07.2013
comment
В любом случае, ссылка имеет лучший, более удобный дизайн и работает более эффективно. В этом смысле он будет лучше масштабироваться, но, конечно, не идеально. - person user1132959; 28.07.2013