Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在线程之间共享字典中的数据_C#_Multithreading_Dictionary - Fatal编程技术网

C# 在线程之间共享字典中的数据

C# 在线程之间共享字典中的数据,c#,multithreading,dictionary,C#,Multithreading,Dictionary,假设代码: class Memory { Dictionary<int, int> m_values; Object lockObject = new Object(); public int GetData(int key) { int result; lock (lockObject) { if (!m_values.TryGetValue(key, out result)) { result = VeryExpensiv

假设代码:

class Memory {
  Dictionary<int, int> m_values;
  Object lockObject = new Object();

  public int GetData(int key) {
    int result;
    lock (lockObject) {
      if (!m_values.TryGetValue(key, out result)) {
        result = VeryExpensiveComputationMethod(key);
        m_values[key] = result;
      }
    }
    return result
  }
}
类内存{
字典m_值;
对象锁定对象=新对象();
公共整型GetData(整型密钥){
int结果;
锁定(锁定对象){
如果(!m_values.TryGetValue(键,输出结果)){
结果=非常昂贵的计算方法(键);
m_值[键]=结果;
}
}
返回结果
}
}

这是安全的,但问题是它不是很有效。如何使用与.NET2.0兼容的代码更好地实现这一点?(在最佳情况下,只有等待相同键的相同结果的线程才应该等待)

如果使用
ConcurrentDictionary
而不是
Dictionary
,则可以获得两个关键改进:

  • 您不需要为显式锁定而烦恼
  • 您有一个(保证是可观察的原子)
    GetOrAdd
    方法,该方法允许您从字典中获取值,或者在不存在值的情况下添加值
  • 结合使用
    Lazy
    ,您可以创建一个对象,该对象将其值定义为昂贵计算的结果,它确保只运行一次,只在需要时运行,然后在反复请求值时缓存并返回一次又一次

    我们现在可以创建一个
    ConcurrentDictionary
    ,在这两种类型之间,它基本上为我们完成了所有工作:

    请注意,一旦我们从
    GetOrAdd
    返回
    Lazy
    ,这意味着:

  • 这是一个新的lazy,它甚至还没有开始计算值,在这种情况下,它将计算值并返回它
  • 它是一个现有的lazy,具有可以立即返回的已计算值
  • 它是一个现有的延迟,其值尚未完成计算;它将等待操作完成,然后该值将返回给等待其计算的所有调用
  • 在任何情况下,都不会对同一密钥多次调用
    VeryExpensiveComputationMethod

    private ConcurrentDictionary<int, Lazy<int>> values =
        new ConcurrentDictionary<int, Lazy<int>>();
    
    public int GetData(int key)
    {
        //Note that this doesn't actually run VeryExpensiveComputationMethod 
        //until .Value is called on it
        var lazy = new Lazy<int>(() => VeryExpensiveComputationMethod(key));
        return values.GetOrAdd(key, lazy).Value;
    }
    
    至于
    Lazy
    ,您只需创建自己的版本:

    public delegate T Func<T>();
    public class Lazy<T>
    {
        private object key = new object();
        private Func<T> generator;
        private T value;
        public Lazy(Func<T> generator)
        {
            this.generator = generator;
        }
    
        private volatile bool hasComputedValue;
        public bool HasComputedValue { get { return hasComputedValue; } }
    
        public T Value
        {
            get
            {
                lock (key)
                {
                    if (HasComputedValue)
                        return value;
                    else
                    {
                        value = generator();
                        hasComputedValue = true;
                        generator = null;
                        return value;
                    }
                }
            }
        }
    }
    
    公共委托T Func();
    公共课懒惰
    {
    私有对象密钥=新对象();
    专用函数生成器;
    私人T值;
    公共事务(函数生成器)
    {
    这个。发电机=发电机;
    }
    私有volatile bool hasComputedValue;
    public bool HasComputedValue{get{return HasComputedValue;}}
    公共价值
    {
    得到
    {
    锁(钥匙)
    {
    如果(HasComputedValue)
    返回值;
    其他的
    {
    值=生成器();
    hasComputedValue=true;
    生成器=空;
    返回值;
    }
    }
    }
    }
    }
    
    考虑到.NET 2.0的限制,一个简单的解决方案是保留一个
    字典,在其中查找锁对象以查找该密钥,然后锁定该对象。因此,锁定更细粒度,因此支持更多并发性。这就是说,您需要另一种锁来处理新的不可见
    int
    的情况

    大概是这样的:

    internal class Memory
    {
        public int GetData(int key)
        {
            int result;
            object locker;
    
            // short time lock, blocks all readers
            lock (lockObject)
                if (!m_locks.TryGetValue(key, out  locker))
                {
                    locker = m_locks[key] = new object();
                }
    
            // long time lock, but only for readers of this key during expensive op
            lock (locker)
                if (!m_values.TryGetValue(key, out result))
                {
                    result = m_values[key] = VeryExpensiveComputationMethod(key);
                }
    
            return result;
        }
    
        private readonly Object lockObject = new Object();
        private Dictionary<int, int> m_values;
        private Dictionary<int, object> m_locks;
    }
    
    内部类内存
    {
    公共整型GetData(整型密钥)
    {
    int结果;
    物体锁;
    //短时间锁定,阻止所有读卡器
    锁定(锁定对象)
    如果(!m_locks.TryGetValue(钥匙,锁柜外))
    {
    locker=m_locks[key]=new object();
    }
    //长时间锁定,但仅适用于昂贵操作期间此密钥的读卡器
    锁(储物柜)
    如果(!m_values.TryGetValue(键,输出结果))
    {
    结果=m_值[键]=非常昂贵的计算方法(键);
    }
    返回结果;
    }
    私有只读对象lockObject=新对象();
    私有字典m_值;
    私有字典m_锁;
    }
    
    Pure.NET2.0解决方案:

    public delegate T Compute<T,TParameter>(TParameter parameter);
    
    public sealed class Lazy<T,TParameter>
    {
      private T m_Result;
      private volatile bool m_IsInitialized;
      private object m_SyncRoot = new object();
      private Compute<T,TParameter> m_Compute;
      private TParameter m_Context;
    
      public Lazy(Compute<T,TParameter> compute, TParameter context)
      {
        if (compute == null)
        {
          throw new ArgumentNullException("compute");
        }
    
        m_Compute = compute;
        m_Context = context;
      }
    
      public T Value
      {
        get
        {
          if (!m_IsInitialized)
          {
            lock (m_SyncRoot)
            {
              if (!m_IsInitialized)
              {
                m_Result = m_Compute.Invoke(m_Context);
                m_Compute = null;
                m_Context = default(TParameter);
                m_IsInitialized = true;
              }
            }
          }
          return m_Result;
        }
      }
    }
    
    class Memory
    {
      static int VeryExpensiveComputationMethod(int key)
      {
        return key;
      }
    
      private Dictionary<int, Lazy<int,int>> m_Values = new Dictionary<int, Lazy<int,int>>();
      private object m_SyncRoot = new object();
    
      public int GetData(int key)
      {
        Lazy<int,int> result;
        lock (m_SyncRoot)
        {
          if (!m_Values.TryGetValue(key, out result))
          {
            result = new Lazy<int,int>(VeryExpensiveComputationMethod, key);
            m_Values[key] = result;
          }
        }
        return result.Value;
      }
    }
    
    公共委托T计算(T参数参数);
    公共密封类
    {
    私人信托结果;
    私人易变资产被初始化;
    私有对象m_SyncRoot=新对象();
    私有计算机mu Compute;
    私有t参数m_上下文;
    公共延迟(计算,TParameter上下文)
    {
    if(compute==null)
    {
    抛出新的ArgumentNullException(“compute”);
    }
    m_Compute=计算;
    m_Context=上下文;
    }
    公共价值
    {
    得到
    {
    如果(!m_已初始化)
    {
    锁定(m_SyncRoot)
    {
    如果(!m_已初始化)
    {
    m_Result=m_Compute.Invoke(m_上下文);
    m_Compute=null;
    m_Context=默认值(t参数);
    m_i初始=真;
    }
    }
    }
    返回m_结果;
    }
    }
    }
    类内存
    {
    静态int VeryExpensiveComputationMethod(int键)
    {
    返回键;
    }
    私有字典m_值=新字典();
    私有对象m_SyncRoot=新对象();
    公共整型GetData(整型密钥)
    {
    懒惰的结果;
    锁定(m_SyncRoot)
    {
    如果(!m_Values.TryGetValue(键,输出结果))
    {
    结果=新的延迟(VeryExpensiveComputationMethod,key);
    m_值[键]=结果;
    }
    }
    返回结果值;
    }
    }
    
    如果您可以升级到.NET4.0及更高版本,您可以利用并发集合,例如ConcurrentBag。这使得代码更加优雅和可读。这本身并不能解决您的问题,如果您不能使用并发存储,我建议使用两个不同的锁,这意味着昂贵的方法可以在锁之外执行。我不能。我使用的是Unity 5.0,它使用的是.NET3.5中的一些内容,但不是全部。不支持.NET 4.0的某些部分看起来它意味着相同的任务,但锁定了实际数据:)不确定您的评论是什么意思。每个字典条目有一个锁对象;满足您的约束,即只有相同条目的读卡器
    public delegate T Compute<T,TParameter>(TParameter parameter);
    
    public sealed class Lazy<T,TParameter>
    {
      private T m_Result;
      private volatile bool m_IsInitialized;
      private object m_SyncRoot = new object();
      private Compute<T,TParameter> m_Compute;
      private TParameter m_Context;
    
      public Lazy(Compute<T,TParameter> compute, TParameter context)
      {
        if (compute == null)
        {
          throw new ArgumentNullException("compute");
        }
    
        m_Compute = compute;
        m_Context = context;
      }
    
      public T Value
      {
        get
        {
          if (!m_IsInitialized)
          {
            lock (m_SyncRoot)
            {
              if (!m_IsInitialized)
              {
                m_Result = m_Compute.Invoke(m_Context);
                m_Compute = null;
                m_Context = default(TParameter);
                m_IsInitialized = true;
              }
            }
          }
          return m_Result;
        }
      }
    }
    
    class Memory
    {
      static int VeryExpensiveComputationMethod(int key)
      {
        return key;
      }
    
      private Dictionary<int, Lazy<int,int>> m_Values = new Dictionary<int, Lazy<int,int>>();
      private object m_SyncRoot = new object();
    
      public int GetData(int key)
      {
        Lazy<int,int> result;
        lock (m_SyncRoot)
        {
          if (!m_Values.TryGetValue(key, out result))
          {
            result = new Lazy<int,int>(VeryExpensiveComputationMethod, key);
            m_Values[key] = result;
          }
        }
        return result.Value;
      }
    }