Проблема с записью в один файл в веб-службе в .NET

Я создал веб-сервис в .net 2.0, С#. Мне нужно записывать некоторую информацию в файл всякий раз, когда клиенты веб-службы вызывают разные методы.

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

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

Решения, которые я пытался реализовать на С# и потерпел неудачу, приведены ниже.

  1. Реализован одноэлементный класс, содержащий код для записи в файл.
  2. Используемый оператор блокировки для переноса кода, который записывает в файл.
  3. Я также пытался использовать log4net с открытым исходным кодом, но это тоже не идеальное решение.
  4. Я знаю о регистрации в системном журнале событий, но у меня нет такого выбора.

Я хочу знать, существует ли идеальное и полное решение такой проблемы?


person pradeeptp    schedule 23.09.2008    source источник


Ответы (11)


Блокировка, вероятно, не работает, потому что ваш веб-сервис запускается более чем одним рабочим процессом. Вы можете защитить доступ с помощью именованного мьютекса, который является общим для процессов, в отличие от блокировок, которые вы получаете с помощью lock(someobject) {...}:

Mutex lock = new Mutex("mymutex", false);

lock.WaitOne();

// access file

lock.ReleaseMutex();
person Khoth    schedule 23.09.2008
comment
Mutex lockobj = новый Mutex (false, mymutex); будет компилировать - person Simon Farrow; 07.07.2009
comment
Вы должны попытаться открыть существующий мьютекс с этим именем, прежде чем просто создавать его. В противном случае эта новая строка Mutex(...) может вызвать исключение. Я также рекомендую использовать блок try/catch/finally, перемещая вызов Release в блок finally и освобождая его только в том случае, если вы действительно получили блокировку. - person Matthew Cole; 01.03.2012

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

Я думаю, что я бы выбрал решение, которое вы сами предложили, Прадип, построить один объект, который делает всю запись в файл журнала. Внутри этого объекта у меня будет очередь, в которую записываются все данные для регистрации. У меня был бы отдельный поток, читающий из этой очереди и записывающий в файл журнала. В среде хостинга с пулом потоков, такой как IIS, создание еще одного потока кажется не слишком приятным, но это только один... Имейте в виду, что очередь в памяти не переживет сбросы IIS; вы можете потерять некоторые записи, которые находятся «в процессе», когда процесс IIS выходит из строя.

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

person Martin    schedule 23.09.2008

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

person Joel    schedule 23.09.2008

Вы можете отправить результаты в очередь MSMQ, а служба Windows выберет элементы из очереди и зарегистрирует их. Он немного тяжеловат, но он должен работать.

person Charles Graham    schedule 23.09.2008

Джоэл и Чарльз. Это было быстро! :)

Джоэл: Когда вы говорите «линия очереди», вы имеете в виду создание отдельного потока, который работает в цикле, чтобы продолжать проверять очередь, а также записывать в файл, когда он не заблокирован?

Чарльз: Я знаю о комбинации MSMQ и службы Windows, но, как я уже сказал, у меня нет другого выбора, кроме как писать в файл из веб-службы :)

спасибо pradeep_tp

person pradeeptp    schedule 23.09.2008

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

Вероятно, проще всего было бы создать поток во время запуска приложения в Global.asax и прослушивать синхронизированную очередь в памяти (System.Collections.Generics.Queue). Пусть поток открыт и владеет временем жизни дескриптора файла, только этот поток может записывать в файл.

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

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

person stephbu    schedule 23.09.2008

Чтобы узнать, что я пытаюсь сделать в своем коде, ниже приведен класс singletone, который я реализовал на С#.

общедоступный закрытый класс FileWriteTest {

private static volatile FileWriteTest instance;

private static object syncRoot = new Object();

private static Queue logMessages = new Queue();

private static ErrorLogger oNetLogger = new ErrorLogger();

private FileWriteTest() { }

public static FileWriteTest Instance
{
    get
    {
        if (instance == null)
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new FileWriteTest();
                    Thread MyThread = new Thread(new ThreadStart(StartCollectingLogs));
                    MyThread.Start();

                }
            }
        }

        return instance;
    }
}

private static void StartCollectingLogs()
{

    //Infinite loop
    while (true)
    {
        cdoLogMessage objMessage = new cdoLogMessage();
        if (logMessages.Count != 0)
        {
            objMessage = (cdoLogMessage)logMessages.Dequeue();
            oNetLogger.WriteLog(objMessage.LogText, objMessage.SeverityLevel);

        }
    }
}

public void WriteLog(string logText, SeverityLevel errorSeverity)
{
    cdoLogMessage objMessage = new cdoLogMessage();
    objMessage.LogText = logText;
    objMessage.SeverityLevel = errorSeverity;
    logMessages.Enqueue(objMessage);

}

}

Когда я запускаю этот код в режиме отладки (имитирует доступ только одного пользователя), я получаю сообщение об ошибке «переполнение стека» в строке, где очередь удалена из очереди.

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

person pradeeptp    schedule 23.09.2008
comment
Зачем использовать volatile, если вы не знаете, как его использовать? ;П - person leppie; 23.09.2008

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

person Silver Dragon    schedule 23.09.2008

Кот,

Я реализовал блокировку Mutex, которая устранила ошибку «переполнения стека». Мне еще нужно провести нагрузочное тестирование, прежде чем я смогу сделать вывод, работает ли он нормально во всех случаях.

Я читал об объектах Mutex на одном из веб-сайтов, где говорится, что Mutex влияет на производительность. Я хочу знать одну вещь с установкой блокировки через Mutex.

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

спасибо pradeep_tp

person pradeeptp    schedule 23.09.2008
comment
Процесс 2 будет ждать завершения процесса 1. Чтобы свести это к минимуму, делайте как можно больше за пределами замка. - person Khoth; 23.09.2008

Он будет ждать, пока мьютекс не будет освобожден....

person CSharpAtl    schedule 23.09.2008

Джоэл: Когда вы говорите «линия очереди», вы имеете в виду создание отдельного потока, который работает в цикле, чтобы продолжать проверять очередь, а также записывать в файл, когда он не заблокирован?

Да, в принципе, я так и думал. Создайте еще один поток с циклом while, пока он не сможет получить доступ к файлу и сохранить его, а затем завершить.

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

person Joel    schedule 23.09.2008