我有这段代码,为了简洁省略了很多事情,但场景是这样的:
public class Billing
{
private List<PrecalculateValue> Values = new List<PrecalculateValue>();
public int GetValue(DateTime date)
{
var preCalculated = Values.SingleOrDefault(g => g.date == date).value;
//if exist in Values, return it
if(preCalculated != null)
{
return preCalculated;
}
// if it does not exist calculate it and store it in Values
int value = GetValueFor(date);
Values.Add(new PrecalculateValue{date = date, value = value});
return value;
}
private object GetValueFor(DateTime date)
{
//some logic here
}
}
我有一个List<PrecalculateValue> Values
我存储了我已计算好的所有值供以后使用,我这样做主要是因为我不想为同一个客户端重新计算两次,每次计算都涉及很多操作并且需要500到1000毫秒,并且很有可能重用该值,因为孔计费类中涉及一些递归。
所有这些都完美无缺,直到我进行测试,我为两个不同的客户端同时进行了两次计算,并且行Values.Single(g => g.date == date).value
返回了一个异常,因为它在集合中找到了多个结果。
所以我检查了列表,并将两个客户端的值存储在同一个列表中。我该怎么做才能避免这个小问题?
答案 0 :(得分:5)
嗯,首先,这一行:
return Values.Single(g => g.date == date).value;
使得后续行永远不会被调用。我猜你在这里稍微解释了你的代码?
如果要将写入同步到Values
列表,最简单的方法是在您修改列表的代码中的任何位置的公共对象上lock
:
int value = GetValueFor(date);
lock (dedicatedLockObject) {
Values.Add(new PrecalculateValue{date = date, value = value});
}
return value;
但是这里有一些值得注意的事情:因为看起来你希望每个PrecalculateValue
有一个DateTime
,更合适的数据结构可能是Dictionary<DateTime, PrecalculateValue>
- 它会提供与DateTime
相比,基于List<PrecalculateValue>
密钥的闪电般快速的O(1)查找,而public class Billing
{
private Dictionary<DateTime, PrecalculateValue> Values =
new Dictionary<DateTime, PrecalculateValue>();
private readonly commonLockObject = new object();
public int GetValue(DateTime date)
{
PrecalculateValue cachedCalculation;
// Note: for true thread safety, you need to lock reads as well as
// writes, to ensure that a write happening concurrently with a read
// does not corrupt state.
lock (commonLockObject) {
if (Values.TryGetValue(date, out cachedCalculation))
return cachedCalculation.value;
}
int value = GetValueFor(date);
// Here we need to check if the key exists again, just in case another
// thread added an item since we last checked.
// Also be sure to lock ANYWHERE ELSE you're manipulating
// or reading from the collection.
lock (commonLockObject) {
if (!Values.ContainsKey(date))
Values[date] = new PrecalculateValue{date = date, value = value};
}
return value;
}
private object GetValueFor(DateTime date)
{
//some logic here
}
}
必须迭代才能找到您要查找的内容。
如果发生这种变化,您的代码可能如下所示:
Single
最后一条建议:除非你的集合中存在一个特定值不超过一个是至关重要的,First
方法是过度的。如果您只是想获得第一个值并忽略可能的重复项,{{1}}更安全(例如,更少的异常机会)和更快(因为它不必遍历整个集合)。
答案 1 :(得分:1)
可以使用类似的东西
public int GetValue(DateTime date)
{
var result = Values.Single(g => g.date == date) ?? GetValueFor(date);
lock (Values)
{
if (!Values.Contains(result)) Values.Add(result);
}
return result.value;
}
private PrecalculateValue GetValueFor(DateTime date)
{
//logic
return new PrecalculateValue() ;
}
建议使用字典作为键值对列表。