是否有一个偏向读取者的ReaderWriterLockSlim等效物?

16
我已经使用了一段时间的ReaderWriterLockSlim,目前它满足了我的需求。随着我不断微调应用程序,我发现ReaderWriterLockSlim 在我的使用场景下略显不足。
根据文档(和我的经验),它更偏向于作者而非读者(即当读者和作者排队等待时,作者会得到优先处理)。然而,我需要一个偏向于读者的等价物。我理解这种组件的副作用(特别是写入器饥饿问题)。
有没有任何现成的等价物可以指出?谢谢。

3
代码的缺失永远无法得出确凿的证据。但是,不,这种情况相当不可能发生。请避免提出购物类问题。 - Hans Passant
如果你想更偏向读取,为什么不用一个更新后的克隆替换内部列表呢?这样你的代码将是无锁的,但更新会稍微重一些。 - jgauffin
1
是的,听起来你可以像jgauffin建议的那样通过复制来解决这个问题,或者如果数据结构太大而无法进行廉价复制,则可以使用写缓冲区使写入频率降低。然后,您可以通过上下调整缓冲区刷新时间来调整读/写平衡。 - Chris Moschini
1
请问您正在保护什么?最近我删除了所有使用ReaderWriterLockSlim的代码,结果非常好。 - Michael Viktor Starberg
1个回答

7
根据MSDN,ReaderWriterLockSlim偏向于写入者。这意味着当读取者和写入者在队列中时,写入者将获得优先权。
这可能会导致读取者饥饿,用于重现的测试代码在此处。 我认为只有在写入是涉及线程上下文切换的长时间操作时才会发生饥饿。至少在我的机器上总是重现,所以如果我错了,请告诉我。
另一方面,来自.net 2.0的ReaderWriterLock既不会产生读取者饥饿,也不会产生写入者饥饿,代价是性能降低。这里是修改后的代码,展示没有发生饥饿。
因此,回到您的问题-这取决于您从RW锁中需要哪些功能。递归锁定、异常处理、超时-最接近生产质量的RW锁,支持所有上述功能,并偏向于读取者,可能是ReaderWriterLock。

您可以采用维基文章描述的第一个读写问题中的代码,但是您需要手动实现上述所有所需功能,并且实现将会出现写者饥饿问题。

锁核心可能看起来像这样:

class AutoDispose : IDisposable 
{ 
  Action _action; 
  public AutoDispose(Action action) 
  { 
    _action = action; 
  }
  public void Dispose()
  {
    _action();
  }
}

class Lock
{
  SemaphoreSlim wrt = new SemaphoreSlim(1);
  int readcount=0;

  public IDisposable WriteLock()
  {
    wrt.Wait();
    return new AutoDispose(() => wrt.Release());
  }

  public IDisposable ReadLock()
  {
    if (Interlocked.Increment(ref readcount) == 1)
        wrt.Wait();

    return new AutoDispose(() => 
    {
      if (Interlocked.Decrement(ref readcount) == 0)
         wrt.Release();
    });
  }
}

比较3种实现的性能,使用3个读取器和3个写入器线程,使用简单的内存操作(使用长时间阻塞操作会导致RWLockSlim出现读取器饥饿,自定义锁出现写入器饥饿):

Performance comparison

我确保编译器没有展开工作负载循环,但可能存在我不知道的其他陷阱,因此这些测量结果仅供参考。测试的源代码在这里


非常好的、非常详细的回答!非常感谢——我会仔细看一下。 - Igor Pashchuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接