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

Posted

技术标签:

【中文标题】是否有全局命名的读/写锁?【英文标题】:Is there a global named reader/writer lock? 【发布时间】:2009-03-12 19:21:23 【问题描述】:

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

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

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

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

【问题讨论】:

简单地尝试打开文件本身然后捕获适当的异常以检查它是否已被锁定有什么问题吗?如果需要的话,反复轮询应该不是问题。到目前为止,这似乎是您最简单的解决方案。 是否可以迁移到数据库解决方案而不是使用文件?我只建议这样做,因为听起来您真正想要的是交易。 【参考方案1】:

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

此锁将允许 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();

虽然很脆弱(每个使用这个更好的进程都有相同的信号量计数!),我认为只要你不尝试太用力,它就会做你想做的事。

【讨论】:

我不太明白。如果您在其中一个应用程序中创建互斥量和信号量,其他应用程序如何访问它? @Trevor:因为它们是操作系统对象。基于访问权限,命名对象是全局可访问的。见msdn.microsoft.com/en-us/library/f55ddskf(v=vs.110).aspx。基本上,命名的 Windows 对象可以跨进程全局访问。 @JimMischel:推荐的释放 writer 互斥锁的时间跨度是多少? @ivorykoder:一般的想法是你持有任何锁的时间越短越好。对于这里的写锁来说尤其如此,因为这是一个排他锁:如果您持有写锁,则没有其他线程或进程可以获得读锁或写锁。所以,获取写入者锁,写入需要写入的数据,然后释放。推荐的时间跨度是“只要你需要,但不再需要”。【参考方案2】:

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

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

【讨论】:

您阅读了问题还是仅阅读了标题?他表示他想避免使用互斥体。 不够好。 :) 修复它。【参考方案3】:

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

【讨论】:

【参考方案4】:

感谢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();
  

【讨论】:

以上是关于是否有全局命名的读/写锁?的主要内容,如果未能解决你的问题,请参考以下文章

了解 javascript 全局命名空间和闭包

MySQL 进阶 锁 -- MySQL锁概述MySQL锁的分类:全局锁(数据备份)表级锁(表共享读锁表独占写锁元数据锁意向锁)行级锁(行锁间隙锁临键锁)

java中的读/写锁

MySQL中的读锁和写锁(InnoDb行锁表锁 MyISAM共享读锁 MyISAM独占写锁)

使用“:: member”引用全局命名空间有什么用处吗?

大厂JAVA核心技能Java中的读/写锁