Существует ли глобальная именованная блокировка чтения / записи?
У меня есть несколько веб-приложений asp.net, обслуживающих набор файлов. Периодически, один обновляет файл перед его обслуживанием, но не может обновить файл, если он используется.
Я мог бы решить эту проблему, используя именованный мьютекс, где имя - путь к файлу (конечно, заменив недопустимые символы). Я использовал это в других ситуациях, но вы можете видеть, насколько это неэффективно. Только один процесс сможет обслуживать файл одновременно.
Блокировка чтения / записи была бы идеальной, но они предназначены для работы в рамках одного процесса. Кроме того, я должен был бы создать блокировку чтения / записи для каждого файла, который может быть обновлен, и их много.
Что мне действительно нужно, так это блокировка чтения / записи, которую можно назвать как мьютекс. Что-то подобное существует? Или такая вещь может быть создана с использованием существующих замков?
5 ответов
Можно смоделировать блокировку чтения / записи, используя Mutex и семафор. Я бы не стал этого делать, если бы мне приходилось обращаться к нему тысячи раз в секунду, но для десятков или, может быть, сотен раз в секунду он должен работать просто отлично.
Эта блокировка позволила бы монопольный доступ 1 писателя или одновременный доступ N (возможно, большим, но вы должны определить это) читателей.
Вот как это работает. Я буду использовать 10 читателей в качестве примера.
Инициализируйте именованный Mutex, изначально неподписанный, и именованный семафор с 10 слотами:
Mutex m = new Mutex(false, "MyMutex");
Semaphore s = new Semaphore(10, 10, "MySemaphore");
Получить блокировку считывателя:
// Lock access to the semaphore.
m.WaitOne();
// Wait for a semaphore slot.
s.WaitOne();
// Release mutex so others can access the semaphore.
m.ReleaseMutex();
Снять блокировку считывателя:
s.Release();
Получить блокировку писателя:
// Lock access to the seamphore
m.WaitOne();
// Here we're waiting for the semaphore to get full,
// meaning that there aren't any more readers accessing.
// The only way to get the count is to call Release.
// So we wait, then immediately release.
// Release returns the previous count.
// Since we know that access to the semaphore is locked
// (i.e. nobody can get a slot), we know that when count
// goes to 9 (one less than the total possible), all the readers
// are done.
s.WaitOne();
int count = s.Release();
while (count != 9)
{
// sleep briefly so other processes get a chance.
// You might want to tweak this value. Sleep(1) might be okay.
Thread.Sleep(10);
s.WaitOne();
count = s.Release();
}
// At this point, there are no more readers.
Отпустите блокировку писателя:
m.ReleaseMutex();
Несмотря на то, что хрупкий процесс (каждый процесс, использующий это, лучше иметь одинаковое число для счетчика семафоров!), Я думаю, он будет делать то, что вы хотите, до тех пор, пока вы не попытаетесь ударить его слишком сильно.
Немного поздно, но некоторое время назад я написал именную блокировку чтения / записи - http://unintelligible.org/blog/2009/10/20/named-reader-writer-lock-in-c/. Это работает, поддерживая карту имени / блокировки ReaderWriterLockSlims и окружая доступ к этой карте в Monitor.
Я ценю хороший ответ Джима Мишеля, но я вижу возможность для улучшения производительности, избегая Thread.Sleep() и избегая конфликта блокировок, когда несколько читателей пытаются получить данные одновременно!
инициализация
Mutex writer = new Mutex(false, "Global\\MyWriterMutex");
Semaphore readers = new Semaphore(int.MaxValue, int.MaxValue, "Global\\MyReadersSemaphore");
EventWaitHandle readAllowed = new EventWaitHandle(true, EventResetMode.ManualReset, "Global\\MyReadAllowedEvent");
EventWaitHandle readFinished = new EventWaitHandle(false, EventResetMode.ManualReset, "Global\\MyReadFinishedEvent");
читатель
while (true)
{
// signal that I'm reading
readers.WaitOne();
// check whether I'm actually allowed to read
if (readAllowed.WaitOne(0))
{
break; // great!
}
// oops, nevermind, signal that I'm not reading
readers.Release();
readFinished.Set();
// block until it's ok to read
readAllowed.WaitOne();
}
try
{
readData();
}
finally
{
// signal that I'm no longer reading
readers.Release();
readFinished.Set();
}
писатель
// block until I am the only writer
try
{
writer.WaitOne();
}
catch (AbandonedMutexException)
{
// The mutex was abandoned in another process, but it was still acquired
}
// signal that readers need to cease
readAllowed.Reset();
// loop until there are no readers
int readerCount = -1;
while (readerCount != 0)
{
// wipe the knowledge that a reader recently finished
readFinished.Reset();
// check if there is a reader
readers.WaitOne();
readerCount = int.MaxValue - (readers.Release() + 1);
if (readerCount > 0)
{
// block until some reader finishes
readFinished.WaitOne();
}
}
try
{
writeData();
}
finally
{
// signal that readers may continue, and I am no longer the writer
readAllowed.Set();
writer.ReleaseMutex();
}
Как насчет этого? Не подавай файлы. Подавать копии файлов. Когда вам нужно внести изменения, создайте новый файл и с тех пор предоставьте его копию.
Я не думаю, что есть что-то, что отвечает вашим потребностям, как указано (хотя я оставляю за собой право ошибаться).
Вы можете подать файл с помощью службы. Это решает две проблемы. Во-первых, как вы заявили, проблема параллелизма. Кроме того, даже если вы можете добиться синхронизации, становится сложнее и уродливее, если вы начнете балансировать нагрузку. Обслуживание файла с помощью службы, скорее всего, будет стоить вам производительности, но решит проблему синхронизации.