3

I know that the int wont have a fixed position in memory so it simply cant work like that. But The exact same portion of code will be run concurrently with different names, parameters e.t.c

I need to essentially pass a string of "Name" and then somehow increment one of the items in my int array.

Dictionary<string, int> intStats = new Dictionary<string, int>();

This dictionary stores all the stats based on the "Name" supplied as the dictionaries string key.

And since im using a LOT of multi-threading, I wish to keep the int count as synchronized as possible. Which is why im attempting to use Interlocked.Increment(ref intStats[theName]); But unfortunately this wont work.

Is there any alternatives that would work for my situation?

  • Can you `lock` the dictionary? Or create a lock dictionary `Dictionary` and lock on the same key while incrementing to reduce contention. – Ron Beyer Mar 10 '18 at 00:07
  • 1
    Do not store `int` directly in your dictionary. Encapsulate the int as well as any atomic operations on this int in a custom class/type. Use object instances of this type for the dictionary values. So you would basically end up with something like `Dictionary` –  Mar 10 '18 at 00:08
  • 1
    Additionally, if you do not/can not completely populate the dictionary with initial values before starting all the threads, it could be preferable to use `ConcurrentDictionary` instead of a "simple" Dictionary. Otherwise you would need to manually sync dictionary access to avoid concurrent adding of new values under the same key. –  Mar 10 '18 at 00:15
  • @RonBeyer I dont currently know how to lock a dictionary like that. –  Mar 10 '18 at 00:20
  • @elgonzo I see what your saying but I dont know how to setup a custom int object class like that. And normal dictionary is fine for my situation. The default value will be pre-defined with the key and a int of 0 before its ever actually being used. –  Mar 10 '18 at 00:21

1 Answers1

3

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I ended up using `Dictionary> stats = new Dictionary>()` and just assigning its default values for each "Name" as String param. Then for the ref, I could do `(ref stats[name].Value)`. Do you recommend your solution over my current one? –  Mar 10 '18 at 02:22
  • @user8549339: `StrongBox` is very similar to `MutableInt`, but its use of properties prevents you from using `InterlockedIncrement`. `Dictionary` is very similar to `ConcurrentDictionary`, but is not thread safe. – Brian Mar 14 '18 at 13:45
  • StrongBox works fine with Interlocked.Increment on my end. And since im not adding or deleting to Dictionary from multiple threads, it works fine. Essentially only the int gets updated. Do you think I should still use ConcurrentDictionary? –  Mar 15 '18 at 07:27