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
,这意味着:
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;
}
}