是否有全局命名的读/写锁?

时间:2009-03-12 19:21:23

标签: .net locking

我有多个asp.net网络应用程序服务于一组文件。在提供文件之前,我们会定期更新文件,但如果文件正在使用,则无法更新文件。

我可以通过使用名为mutex来解决此问题,其中名称是文件路径(当然替换无效字符)。我在其他情况下使用过这个,但你可以看到效率低下。一次只能有一个进程提供文件。

读取器/写入器锁定是完美的,但它们被设计为在单个进程中工作。另外,我必须为每个可能更新的文件创建一个读/写锁,而且还有很多。

我真正需要的是一个可以像互斥锁一样命名的读/写锁。有这样的事吗?或者可以使用现有的锁创建这样的东西吗?

5 个答案:

答案 0 :(得分:19)

可以使用Mutex和Semaphore模拟读/写锁。如果我每秒必须访问它数千次,我不会这样做,但是每秒数十次或者数百次,它应该可以正常工作。

这个锁允许1个作者独占访问或N个并发访问(可能很大,但你必须定义它)读者。

这是它的工作原理。我将以10位读者为例。

初始化一个名为Mutex,最初没有信号,一个名为Semaphore的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();

虽然很脆弱(使用它的每个进程都有相同数量的信号量计数!),但我认为只要你不努力击中它就会做你想要的。

答案 1 :(得分:3)

有点晚了,但我刚才写了一个命名的读者/作家锁 - http://unintelligible.org/blog/2009/10/20/named-reader-writer-lock-in-c/。这可以通过维护ReaderWriterLockSlims的名称/锁定映射以及在监视器中对该映射的周围访问来实现。

答案 2 :(得分:1)

我认为没有任何东西可以满足您的要求(尽管我保留错误的权利)。

您可以使用服务提供文件。这解决了两个问题。首先,正如您所说,并发问题。此外,即使您可以实现同步,如果您开始负载平衡,也会变得更加困难和丑陋。使用服务提供文件可能会降低性能,但可以解决同步问题。

答案 3 :(得分:1)

这个怎么样?不要提供文件。提供副本文件。当您需要进行更改时,请创建一个新文件,然后提供该文件的副本。

答案 4 :(得分:1)

我感谢Jim Mischel's fine answer,但是当多个读者试图同时获取时,我通过避免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();
  }