在线程中锁定或尝试捕获字典是否更有效(.NET)

Ric*_*ick 6 .net multithreading dictionary locking try-catch

我有一个通用的词典,我用它作为线程.NET项目(C#)中的缓存.我将在字典上进行大量读取(在高峰时间可能每秒数百或更多).

我有一个简单的Get(int id)方法,如果它在缓存中,则返回一个CustomObject,否则返回null.

我的问题:锁定字典更快/更有效,还是只使用try/catch块?

假设字典存储在名为"dic"的变量中.

锁定样本:

public CustomObject Get(int id)
{
   lock(dic)
   {
      if (dic.ContainsKey(id))
         return dic[id];
   }
   return null;
}
Run Code Online (Sandbox Code Playgroud)

试试/抓住样本:

public CustomObject Get(int id)
{
   try
   {
      return dic[id];
   }
   catch
   {
      return null;
   }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*uła 8

我认为你应该在自己的环境中测试它.基本上:

  • 锁很便宜
  • 尝试没有得到一个例外便宜,甚至可能更便宜锁定
  • 尝试并获得异常是非常昂贵的

所以现在的问题是,你有多少次期望有缓存缺失,因此会抛出异常.我会选择lock(),因为它的执行时间不依赖于你是否会得到缓存命中,这意味着它更可预测和可测量,同时仍然非常便宜.我不认为每秒数百次点击会有任何问题.

我做的简单测试表明,使用try/catch获取缓存失败是非常非常昂贵的.

编辑:

简单测试表明:

  • 对于10万次检索,尝试不投入大约2毫秒
  • 锁定成本大约6ms
  • try-throw花费大约4秒

这意味着,获得了lock(),因为它比try/catch更有效,如果你每几千次尝试获得超过1次缓存未命中,并且它更加稳定,不依赖于运气.


Bri*_*eon 6

您可以继续并注销try-catch选项.我不知道它是否更慢,但我知道如果有另一个线程更新,它将不会总是产生正确,一致和可预测的结果Dictionary.问题在于,在某些时候,作者将Dictionary处于半生不熟的状态,而且没有人知道读者会看到什么.这不行.

选项1:如果您可以使用.NET 4.0,那么我会使用ConcurrentDictionary.

选项2:如果您使用的是.NET 3.5,则可以下载Reactive Extensions backport.ConcurrentDictionary包含在System.Threading.dll中.

选项3:另一个想法是保留两个单独的副本Dictionary.一个只用于阅读,而另一个用作接受更新的官方副本.无论何时更新"官方",Dictionary您都会克隆它并覆盖副本的引用.

public class Example
{
  // This is the official version which can accept updates.
  private readonly Dictionary<int, CustomObject> official = new Dictionary<int, CustomObject>();

  // This is a readonly copy. This must be marked as volatile for this to work correctly.
  private volatile Dictionary<int, CustomObject> copy = new Dictionary<int, CustomObject>();

  public class Example()
  {
  }

  public void Set(int id, CustomObject value)
  {
    lock (official)
    {
      // Update the official dictionary.
      official[id] = value;

      // Now create a clone of the official dictionary.
      var clone = new Dictionary<int, CustomObject>();
      foreach (var kvp in official)
      {
        clone.Add(kvp.Key, kvp.Value);
      }

      // Swap out the reference. 
      copy = clone;
    }
  }

  public CustomObject Get(int id)
  {   
    // No lock is required here.
    CustomObject value = null;
    if (copy.TryGetValue(id, out value))
    {
      return value;
    }
    return null;
  }

}
Run Code Online (Sandbox Code Playgroud)

如果有很多项目Dictionary或者如果经常发生官方副本的更新,则此选项不能正常工作.但是,这是我不时使用的一个技巧.

方案4:同样合理的方法是坚持使用普通的老方法lock.