我正在将对象添加/更新到并发字典中并定期(每分钟)刷新字典,所以我的代码看起来像这样:
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();
}
Run Code Online (Sandbox Code Playgroud)
现在我不确定这段代码是否有可能丢失一些对象/更新
Jon*_*eet 15
是的,你可能会丢失一些数据:
_metrics领域,并得到了老字典,然后被中断_metrics用新字典替换该字段Values.ToArray()AddOrUpdate不再被任何东西查看的字典.(它在步骤1中获取的那个)换句话说,想象一下你的IncrementMetricCountBy方法实际上是:
public static void IncrementCountMetricBy(string name, int count)
{
var tmp = _metrics;
Thread.Sleep(1000);
tmp.AddOrUpdate(...);
}
Run Code Online (Sandbox Code Playgroud)
如果您可以看到为什么不安全,则相同的参数适用于您当前的代码.
据我所知,这里没有什么特别简单的事可做ConcurrentDictionary.一种选择是拍摄所有键的快照,然后将它们全部删除:
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;
Run Code Online (Sandbox Code Playgroud)
返回时字典可能不为空,但您不应丢失任何数据.(自方法启动以来,您可能会更新度量标准,并且在删除密钥后发生的任何更新将最终重新添加,但这应该没问题.)
它是.考虑以下情况:
线程一次调用AddOrUpdate,但在调用开始后立即停止执行,然后在该方法中执行任何操作(包括取出任何锁定).
线程两个副本字典的所有值.
线程一回去完成添加项目.
那个项目就会丢失.