0

In a multithreaded application, I have a Dictionary that is accessed by multiple threads for gettinng the value for a specific key. There is also a mechanism using Quartz.Net to update this dictionary. I am trying to find the best way to make the updated Dictionary availiable for reading in a thread safety manner. Initialy I considered a ReadWriterLockSlim as a solution, but as I searched for probable performance penalties it might have I came upon Interlocked.Exchange and an overload that can be used with objects. So my question is, could it be used in this scenario? I present a code sample of it's usage.

Thanks very much

public class SingletonDictionaryHolder
{
    private Dictionary<string, Person> MyDictionary;

    public Person GetPerson(string key)
    {
        return MyDictionary[key];
    }

    public void UpdateDictionary(Dictionary<string, Person> updated)
    {
        Interlocked.Exchange(ref MyDictionary, updated);
    }
}  

 

Edit:

Since there is a downvote, I am adding some more information:

Another relative questions is presentated here: https://softwareengineering.stackexchange.com/questions/294514/does-readerwriterlockslim-provide-thread-safety-and-speed-efficiency-compared-t

Note the paragraph: "If writes are rare, you can probably get much better performance by treating the collection as immutable and producing an entirely new list when it changes. You can then use Interlocked.CompareExchange to update the reference to the list in a thread-safe way. This should prevent readers needing to make a defensive copy (which should be a huge win if reading is more common than writing) and remove the need for locking."

And concearning the Intelocked.CompareExchange method, an insight is presented here: Using Interlocked.CompareExchange with a class

Kindly note that a correct architectural design would be to use a MemoryCache that is thread safe by default and a pub/sub mechanism to reflect changes on the cached items - however it was not designed by me and I doubt that there is hope of change in the near future.

fdhsdrdark
  • 144
  • 1
  • 13
  • please don't try to write your own synchronization scheme, most likely you'll shoot yourself in the foot. – David Haim Dec 12 '20 at 14:40
  • I would never try that, I am searching the best option. Interlocked.CompareExchange VS ReaderWriterLockSlim. – fdhsdrdark Dec 12 '20 at 17:01
  • 1
    Are the dictionaries immutable after their creation? – Theodor Zoulias Dec 12 '20 at 17:21
  • @TheodorZoulias Yes, the dictionaries won't change – fdhsdrdark Dec 12 '20 at 18:54
  • It's safe (but why not use an ImmutableDictionary?). Bear in mind that accesses to the dictionary field may see the old dictionary there for a time. – canton7 Dec 12 '20 at 19:53
  • @canton7 You could definitely use an Immutable Dictionary, but you then have to use it's Add/Remove methods to reflect the changes. Therefore you have to search the Dictionary for added/removed/updated entries. In a Dictionary with thousands of entries that would have a cost. A better option if you like would be a ReadOnlyDictionary that will be "overwritten" with an updated one. – fdhsdrdark Dec 12 '20 at 20:07
  • Is it possible that multiple threads may attempt to replace the `original` with an `updated` dictionary concurrently? If so, are you OK with the possibility of multiple threads generating an `updated` dictionary concurrently, but only one thread replaces the `original`, and the dictionaries created by the other threads are discarded? – Theodor Zoulias Dec 12 '20 at 20:33
  • @Theodor Zoulias Normally just one thread will update the original since the update is done through a Quartz.Net Job. Quartz.net Jobs have an option of disallowing concurrent execution. As a result, one update is happening at a time every one hour for example. – fdhsdrdark Dec 12 '20 at 21:05
  • In that case I think that no synchronization is needed. Just mark the field as `volatile` so that it's visible by all threads, and you are ready to go. – Theodor Zoulias Dec 12 '20 at 21:56
  • @TheodorZoulias Thanks for keeping up on this! I think that synchronization is required. Single writer and multiple readers on a shared resource require a synchronization mechanism. ReaderWriterLockSlim is one option to achive that. I have updated the question with some code and comments to make it even clearer. As far as the volatile is concrearned, I don't think it completely required - after the successful update on the Dictionary every thread will access the updated value. – fdhsdrdark Dec 13 '20 at 07:37
  • [Reference assignment is guaranteed to be atomic on all .NET platforms](https://stackoverflow.com/questions/2192124/reference-assignment-is-atomic-so-why-is-interlocked-exchangeref-object-object/2193445#2193445). So the `Interlocked.Exchange` in your (updated) question is redundant regarding thread-safety, but it is useful regarding thread-visibility. It is equivalent to a `Volatile.Write`. But since the readers of the `MyDictionary` field are reading it without memory fences, they are not guaranteed to see the current value of the field in the main memory. They may see a stale value instead. – Theodor Zoulias Dec 13 '20 at 07:52
  • @TheodorZoulias Hmmm, a bit confused here. The comment on this https://stackoverflow.com/questions/11745440/what-operations-are-atomic-in-c says : "Reference reads and reference writes are atomic; a reference assignment combines an (atomic) reference read and an (atomic) reference write, but the operation as a whole is not atomic." – fdhsdrdark Dec 13 '20 at 07:59
  • You should ask @supercat that wrote [that](https://stackoverflow.com/questions/11745440/what-operations-are-atomic-in-c#comment45532314_11745701) comment! AFAIK a reference assignment is a write operation, not a read-write operation. – Theodor Zoulias Dec 13 '20 at 08:09
  • @TheodorZoulias Will do! Please post an answer on my question providing the link about Reference assignement and I will mark it as the accepted answer. I will update the comments on the code on my question for future readers. Huge Thanks! – fdhsdrdark Dec 13 '20 at 08:14
  • 1
    Nahh, I don't think that plagiarizing Eric Lippert would make for a good answer on my part. I suggest to [answer your question](https://stackoverflow.com/help/self-answer) yourself, because you have a better knowledge of what you are trying to do, and you could give a more useful answer for future readers. – Theodor Zoulias Dec 13 '20 at 08:21

1 Answers1

0

Answering my own question, guided by the really helpfull comments. Interlock.Exchange in not necessary for thread safety since reference assignment is thread safe in all .Net platforms.

So the updated object can be safely assigned to the original one. Threads that will access the object in question after the update will get the fresh new one, something that is completely fine for my scenario.

For future readers coming across this question, please have a look on this: reference assignment is atomic so why is Interlocked.Exchange(ref Object, Object) needed?

fdhsdrdark
  • 144
  • 1
  • 13