【问题标题】:Error with ReaderWriterLockSlimReaderWriterLockSlim 出错
【发布时间】:2012-04-24 17:45:18
【问题描述】:

我遇到了这个异常
读锁被释放而不被持有。
在 System.Threading.ReaderWriterLockSlim.ExitReadLock() 在..GetBreed(String)

下面是代码中唯一可以访问锁的地方。如您所见,没有递归。我无法理解这个异常是如何发生的。

static readonly Dictionary<string, BreedOfDog> Breeds 
     = new Dictionary<string,BreedOfDog>();

static BreedOfDog GetBreed(string name)
{
        try
        {
            rwLock.EnterReadLock();
            BreedOfDog bd;
            if (Breeds.TryGetValue(name, out bd))
            {
                return bd;
            }
        }
        finally
        {
            rwLock.ExitReadLock();
        }

        try
        {
            rwLock.EnterWriteLock();
            BreedOfDog bd;
            //make sure it hasn't been added in the interim
            if (Breeds.TryGetValue(t, out bd)
            {
                return bd;
            }
            bd = new BreedOfDog(name); //expensive to fetch all the data needed to run  the constructor, hence the caching

            Breeds[name] = bd;
            return bd;
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
}  

【问题讨论】:

  • 你能制作一个简短但完整的程序来演示这个问题吗?
  • GetBreed 从不同的线程运行?
  • 你不应该获取锁,放手,然后获取它。您应该从头到尾保持锁定并使用可升级的读锁,然后如果需要写入则升级。
  • @vcsjones 我不同意 - 在大多数情况下,释放并重新获取(也许仔细检查)更有效
  • @spender 是的,它从不同的线程运行

标签: c# multithreading readerwriterlockslim


【解决方案1】:

我猜你有一些可重入的东西,它在获取锁时抛出异常。无论您“获取锁”、“尝试”还是“尝试”、“获取锁”都有一个 catch-22,但是“获取锁”、“尝试”的失败案例较少(“获取和尝试”的可能性非常小,您无需强调)。

将“取锁”移到“尝试”之外,看看实际异常是什么。

问题很可能是您未能获得锁(可能是重新进入),然后试图解锁您没有获得的东西。这可能意味着在获取锁的原始代码中出现异常,因为在只获取一次时尝试释放两次。

注意:Monitor 具有带有“ref bool”参数的新重载来帮助解决这种情况 - 但不是其他锁类型。

【讨论】:

    【解决方案2】:

    在实例化 RWLS 时使用 LockRecursionPolicy.SupportsRecursion。如果错误消失,那么您实际上确实涉及某种类型的递归。也许是您没有发布的代码?

    如果您真的很关心从中获得最大并发性(我怀疑您是因为您使用的是 RWLS),那么您可以使用双重检查锁定模式。请注意您的原始代码已经有了这种感觉?那么为什么要绕圈子呢?去做吧。

    请注意,在下面的代码中,我始终将 Breeds 引用视为不可变引用,然后在 lock 内部重新检查、复制、更改和交换引用。

    static volatile Dictionary<string, BreedOfDog> Breeds = new Dictionary<string,BreedOfDog>();
    static readonly object LockObject = new object();
    
    static BreedOfDog GetBreed(string name)
    {
      BreedOfDog bd;
      if (!Breeds.TryGetValue(name, out bd))
      {
        lock (LockObject)
        {
          if (!Breeds.TryGetValue(name, out bd))
          {
            bd = new BreedOfDog(name);
            var copy = new Dictionary<string, BreedOfDog>(Breeds);
            copy[name] = bd;
            Breeds = copy;
          }
        }
      }
      return bd;
    }
    

    【讨论】:

    • 很好的答案。我对所有 RWLS 都采用同样的方法。
    • @BitKFu:没那么好。我删除了我的 RWLS 版本,因为它根本不是并发的。 EnterUpgradeableReadLock 基本上会序列化 GetBreed 方法。
    • 嗯。将查看我已实施的解决方案。
    猜你喜欢
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    相关资源
    最近更新 更多