#c# #concurrency #filesystems #writer
#c# #параллелизм #файловые системы #запись
Вопрос:
Существует служба, которая постоянно записывает новое содержимое в файл:
using (var stream = File.Create(FileName)) // overwrites the file
{
stream.Write(data, 0, data.Length);
}
К файлу постоянно обращаются несколько читателей (включая веб-приложение, которое отображает его содержимое из этого файла). У меня нет контроля над кодом клиентов readers. Файл всегда должен быть доступен для читателей. Более того, они должны видеть содержимое целиком, а не содержимое в середине записи в файл.
Любые методы, подобные этому:
using (var stream = File.Create(FileName ".tmp"))
{
stream.Write(data, 0, data.Length);
}
File.Delete(FileName);
File.Move(FileName ".tmp", FileName);
может привести к отсутствию содержимого на веб-странице (с некоторой вероятностью). И служба иногда выдает IOException
исключение с сообщением «Процесс не может получить доступ к файлу, поскольку он используется другим процессом».
Вопрос в следующем: как можно постоянно заменять содержимое файла без прерывания доступа клиентов readers?
Комментарии:
1. Я совершенно уверен, что это невозможно.
2. Синхронизируя доступ для чтения к файлу с глобальным мьютексом?
3. @Tergiver: К сожалению, нет контроля над кодом клиентов readers.
4. как в вашем коде Reader размещен IIS, сервис, настольное приложение или что-то еще?
Ответ №1:
В IIS вы могли бы адаптировать этот модуль (полное раскрытие, я его написал) для внедрения синхронизации в запросы на чтение. Вы могли бы сделать это, создав подкласс InterceptingHandler и добавив код типа:
SychronizingHandler : InterceptingHandler
{
// ...
Semaphore mySemaphore;
protected override bool PreFilter(System.Web.HttpContext context)
{
context.RewritePath("myFilePath");
if( mySemaphore == null)
{
bool created;
mySemaphore = new Semaphore(100, 0, "semphoreName", out created);
}
if( mySemaphore != null)
{
mySemaphore.WaitOne();
}
reutrn true;
}
// note this function isn't in the base class
// you would need to add it and call it right after the call to
// innerHandler.ProcessRequest
protected override void PostFilter(System.Web.HttpContext context)
{
mySemaphore.Release();
return;
}
protected virtual void OnError(HttpContext context, Exception except)
{
mySemaphore.Release();
return base.OnError(context, except);
}
Настольное приложение немного сложнее, поскольку оно зависит от деталей реализации приложения. Надеюсь, в этом случае у вас есть какой-то способ расширить его и добавить синхронизацию.
Как отмечает Фан в комментариях, вы также могли бы выполнить условную перезапись в своем предварительном фильтре, чтобы не пытаться получить доступ к записываемому файлу, что является очень хорошей идеей.
Комментарии:
1. Это может сработать, если настольное приложение может читать непосредственно из IIS. В качестве альтернативы вы могли бы использовать дескриптор, обслуживающий файл A во время записи файла B, и обслуживающий файл B во время записи файла A. Таким образом, вы избежите слишком длительной блокировки.
2. Спасибо за ваш ответ. К сожалению, у меня нет контроля над кодом клиентов Reader.