Ответ 1
Да, вы можете потерять некоторые данные там:
- Инкрементный поток может прочитать поле
_metrics
и получить старый словарь, а затем прерваться - Затем поток смыва заменяет поле
_metrics
новым словарем - Поток для очистки, называемый
Values.ToArray()
- Приращающийся поток затем вызывает
AddOrUpdate
в словаре, на который больше ничего не смотрит. (Тот, который он выбрал на шаге 1.)
Другими словами, предположим, что ваш метод IncrementMetricCountBy
на самом деле:
public static void IncrementCountMetricBy(string name, int count)
{
var tmp = _metrics;
Thread.Sleep(1000);
tmp.AddOrUpdate(...);
}
Если вы видите, почему это небезопасно, тот же аргумент применяется в вашем текущем коде.
Насколько я вижу, нет ничего особенного, что вы можете сделать с 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;
Словарь может не быть пустым при возврате, но вы не должны терять данные. (Вы можете обновлять показатели с момента запуска метода, и любое обновление, которое происходит после удаления ключа, закончится повторным добавлением, но это должно быть хорошо.)