c# - 从并发字典中获取所有值并在不丢失数据的情况下清除它

标签 c# .net

我正在向并发字典中添加/更新对象并定期(每分钟)刷新字典,所以我的代码看起来像这样:

    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();
    }

现在我不确定这段代码是否有可能丢失一些对象/更新

最佳答案

是的,您可能可能在那里丢失一些数据:

  1. 递增线程可以读取 _metrics 字段并获取旧字典,然后被中断
  2. 刷新线程然后用新字典替换_metrics字段
  3. 刷新线程调用了 Values.ToArray()
  4. 递增线程然后在字典上调用 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/

相关文章:

.net - 如何转换 lambda 表达式?

C# 以 int 数组作为索引过滤循环的更快方法?

C# 在泛型列表中使用 Equals 方法失败

c# - 在 VB.NET 中创建可从 C# 使用的索引器

c# - PrintQueue.AddJob() 停止执行

c# - 从 Windows 身份验证中获取 `ICredentials`

c# - 这个简单的更新查询有什么问题?

c# - 为什么我不需要调用非 GUI 线程的某些表单元素?

c# - 以编程方式向 TFS 添加新文件

C# 二进制搜索变体