Runtime: Add a new API(GetOrAddSafe) for the Concurrentdictionary type?

Created on 18 Mar 2020  路  5Comments  路  Source: dotnet/runtime

ConcurrentDictionary is a thread safe dictionary.
When users call GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) at the same time, valuefactory will execute before the dictionary is locked due to security policy, which also means that it may be expensive due to the concurrent execution of valuefactory.
If the user can ensure that the valuefactory must be a safe code (it will not conflict with the lock inside the dictionary), can an API be provided to execute the valuefactory after the dictionary lock?

ConcurrentDictionary.GetOrAdd

area-System.Collections

Most helpful comment

BTW the most reliable solution in this situation is to use a ConcurrentDictionary<..., Lazy<T>> as recommended at https://github.com/dotnet/runtime/issues/33221#issuecomment-595089012. Your _valueFactory_ would create a Lazy<T> instance, and each Lazy<T> instance would be evaluated _outside_ the dictionary (as a singleton) by whoever is calling into the dictionary. You can also pass whatever parameters you need into the Lazy<T> ctor such that it executes with your desired concurrency model.

All 5 comments

It's not a security policy - it's a reliability issue. The docs explain it in a bit more detail.

the valueFactory delegate is called outside the locks to avoid the problems that can arise from executing unknown code under a lock

If you wanted an API that allowed executing such code under the dictionary's own lock, the API would really be _GetOrAddUnsafe_, not _GetOrAddSafe_.

BTW the most reliable solution in this situation is to use a ConcurrentDictionary<..., Lazy<T>> as recommended at https://github.com/dotnet/runtime/issues/33221#issuecomment-595089012. Your _valueFactory_ would create a Lazy<T> instance, and each Lazy<T> instance would be evaluated _outside_ the dictionary (as a singleton) by whoever is calling into the dictionary. You can also pass whatever parameters you need into the Lazy<T> ctor such that it executes with your desired concurrency model.

BTW the most reliable solution in this situation is to use a ConcurrentDictionary<..., Lazy<T>> as recommended at #33221 (comment). Your _valueFactory_ would create a Lazy<T> instance, and each Lazy<T> instance would be evaluated _outside_ the dictionary (as a singleton) by whoever is calling into the dictionary. You can also pass whatever parameters you need into the Lazy<T> ctor such that it executes with your desired concurrency model.

I initially considered using Lazy<T> to implement, but this is just a way to use design patterns to make tricks, it just makes the cost of creating a valueFactory concurrently smaller. If you come back to the problem itself, it still needs to create multiple Lazy objects (and getting values from Lazy also becomes relatively expensive), so this requirement is to make the execution of multiple valuefactory become the execution of one valuefactory.

@1996v - the problem is that the majority cases fall into two buckets:

  1. The created objects are cheap (ie, primitives or small structs), so the case where multiple values are created and destroyed are not concerning.
  2. The chance of concurrent access for the same key is low, so the case where multiple values are created is rare - the probability that the func is called a second time is low.

Which is why the recommendation.

In light of the above, I think it makes sense to close this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  路  3Comments

matty-hall picture matty-hall  路  3Comments

noahfalk picture noahfalk  路  3Comments

omariom picture omariom  路  3Comments

nalywa picture nalywa  路  3Comments