First, I suggest creating a custom type that captures the semantics of your abstract data type. That way you can experiment with different implementations, and that way your call sites become self-documenting.
internal sealed class NameCounter
{
public int GetCount(string Name) { ... }
public void Increment(string Name) { ... }
}
So: what implementation choices might you make, given that this must be threadsafe?
a private Dictionary<string, int>
would work but you'd have to lock the dictionary on every access, which could get expensive.
a private ConcurrentDictionary<string, int>
, but keep in mind that you have to use TryUpdate
in a loop to make sure you don't lose values.
make a wrapper type:
internal sealed class MutableInt
{
public int Value;
}
This is one of the rare cases when you'd want to make a public field. Now make a ConcurrentDictionary<string, MutableInt>
, and then InterlockedIncrement
the public field. Now you don't have to TryUpdate
, but there is still a race here: if two threads both attempt to add the same name at the same time for the first time then you have to make sure that only one of them wins. Use AddOrUpdate
carefully to ensure that this race doesn't happen.
Implement your own concurrent dictionary as a hash table that indexes into an int array; InterlockedIncrement
on elements of the array. Again, you'll have to be extremely careful when a new name is introduced into the system to ensure that hash collisions are detected in a threadsafe manner.
Hash the string to one of n buckets, but this time the buckets are immutable dictionaries. Each bucket has a lock; lock the bucket, create a new dictionary from the old one, put it back in the bucket, unlock the bucket. If there is contention, increase n until it goes away.