I am adding/updating objects into a concurrent dictionary and periodically (every minute) flushing the dictionary, so my code looks something like this:
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();
}
now I'm not sure if it's possible for this code to lose some objects/updates
Also, although all methods of ConcurrentDictionary<TKey,TValue> are thread-safe, not all methods are atomic, specifically GetOrAdd and AddOrUpdate. To prevent unknown code from blocking all threads, the user delegate that's passed to these methods is invoked outside of the dictionary's internal lock.
It is thread safe in your usage. It becomes not thread safe when the delegate passed to AddOrUpdate has side effects, because those side effects may be executed twice for the same key and existing value.
ConcurrentDictionary is thread-safe collection class to store key/value pairs. It internally uses locking to provide you a thread-safe class. It provides different methods as compared to Dictionary class. We can use TryAdd, TryUpdate, TryRemove, and TryGetValue to do CRUD operations on ConcurrentDictionary.
Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently.
Yes, you could potentially lose some data there:
_metrics
field and get the old dictionary, and then be interrupted_metrics
field with the new dictionaryValues.ToArray()
AddOrUpdate
on a dictionary which is no longer being looked at by anything. (The one that it fetched in step 1.)To put it another way, imagine that your IncrementMetricCountBy
method is actually:
public static void IncrementCountMetricBy(string name, int count)
{
var tmp = _metrics;
Thread.Sleep(1000);
tmp.AddOrUpdate(...);
}
If you can see why that isn't safe, the same argument applies in your current code.
As far as I can see there isn't anything particularly simple that you can do with ConcurrentDictionary
here. One option would be to take a snapshot of all the keys, and then remove them all:
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;
The dictionary may not be empty when you return, but you shouldn't lose any data. (You may get metrics updated since the method started, and any update which happens after a key has been removed will end up re-adding it, but that should be okay.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With