我正在向并发字典中添加/更新对象并定期(每分钟)刷新字典,所以我的代码看起来像这样:
private static ConcurrentDictionary<string, Metric> _metrics = new ConcurrentDictionary<string, Metric>();
public static void IncrementCountMetricBy(string name, int count)
{
_metrics.AddOrUpdate(....
}
public static Metric[] Flush()
{
var flushedMetrics = _metrics;
_metrics = new ConcurrentDictionary<string, Metric>();
return flushedMetrics.Values.ToArray();
}
现在我不确定这段代码是否有可能丢失一些对象/更新
最佳答案
是的,您可能可能在那里丢失一些数据:
- 递增线程可以读取
_metrics
字段并获取旧字典,然后被中断 - 刷新线程然后用新字典替换
_metrics
字段 - 刷新线程调用了
Values.ToArray()
- 递增线程然后在字典上调用
AddOrUpdate
,该字典不再被任何对象查看。 (它在第 1 步中获取的那个。)
换句话说,假设您的 IncrementMetricCountBy
方法实际上是:
public static void IncrementCountMetricBy(string name, int count)
{
var tmp = _metrics;
Thread.Sleep(1000);
tmp.AddOrUpdate(...);
}
如果您明白为什么这样做不安全,那么同样的论点也适用于您当前的代码。
据我所知,您可以在此处使用 ConcurrentDictionary
做任何特别简单的事情。一种选择是拍摄所有 key 的快照,然后将它们全部删除:
var keys = _metrics.Keys.ToList();
var values = new List<Metric>();
foreach (var key in keys)
{
Metric metric;
if (_metrics.TryRemove(key, out metric))
{
values.Add(metric);
}
}
return values;
返回时字典可能不是空,但您不应丢失任何数据。 (您可能会在该方法启动后更新指标,并且在删除 key 后发生的任何更新最终都会重新添加它,但这应该没问题。)
关于c# - 从并发字典中获取所有值并在不丢失数据的情况下清除它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17195007/