Существует ли глобальная именованная блокировка чтения / записи? - PullRequest
14 голосов
/ 12 марта 2009

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

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

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

Что мне действительно нужно, так это блокировка чтения / записи, которую можно назвать как мьютекс. Что-то подобное существует? Или можно создать такую ​​вещь, используя существующие блокировки?

Ответы [ 5 ]

19 голосов
/ 12 марта 2009

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

Эта блокировка позволила бы монопольный доступ 1 писателя или одновременный доступ N (возможно, большим, но вы должны определить это) читателей.

Вот как это работает. Я буду использовать 10 читателей в качестве примера.

Инициализирует именованный мьютекс, изначально не сигнализированный и именованный семафор с 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();

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

3 голосов
/ 10 января 2011

Немного поздно, но я недавно написал именованную блокировку чтения / записи - http://unintelligible.org/blog/2009/10/20/named-reader-writer-lock-in-c/. Это работает, поддерживая карту имени / блокировки ReaderWriterLockSlims и окружая доступ к этой карте в Monitor.

1 голос
/ 18 августа 2017

Я ценю Прекрасный ответ Джима Мишеля , но я вижу возможность для повышения производительности, избегая 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");

Reader

  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();
  }
1 голос
/ 13 марта 2009

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

1 голос
/ 12 марта 2009

Я не думаю, что есть что-то, что отвечает вашим потребностям, как указано (хотя я оставляю за собой право ошибаться).

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

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