Improved Thread locking advice needed Improved Thread locking advice needed multithreading multithreading

Improved Thread locking advice needed


It looks like you're using this code to populate a dictionary in a thread-safe manner - could you use a ConcurrentDictionary instead?

class A {  private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>();  private int _id;  private object GetObject() {    object output = null;    if(_dictionary.TryGetValue(_id, output)) {      return output;    } else {      return _dictionary.GetOrAdd(_id, CreateTestObject(_id));    }  }}

Edit: If you want to completely eliminate the possibility of invoking duplicate CreateTestObject methods then you can store a wrapper in _dictionary that lazily sets object

class Wrapper {  private volatile object _obj = null;  public object GetObj() {    while(_obj == null) {      // spin, or sleep, or whatever    }    return _obj;  }  public void SetObj(object obj) {    _obj = obj;  } }class A {  private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>();  private int _id;  private object GetObject() {    Wrapper wrapper = null;    if(_dictionary.TryGetValue(_id, wrapper)) {      return wrapper.GetObj();    } else {      Wrapper newWrapper = new Wrapper();      wrapper = _dictionary.GetOrAdd(_id, newWrapper);      if(wrapper == newWrapper) {        wrapper.SetObj(CreateTestObject(_id));      }      return wrapper.GetObj();    }  }}

Only one thread will be able to put a new Wrapper in _dictionary at the specified _id - that thread will initialize the object inside of the wrapper == newWrapper conditional. Wrapper#GetObj spins until the object is set, this can be rewritten to block instead.


This can't work, because Monitor (which is used internally by the lock statement) is re-entrant. That means that a thread can enter any lock it already owns any number of times.

You could solve this by using a Semaphore instead of a Monitor, but stop for a while and listen to what you're asking - you want the thread to block on a lock owned by that same thread. How is that thread ever going to wake up? It will deadlock forever - waiting for the lock to be released, while also being the one holding the lock.

Or are you just trying to handle lazy initialization of some object without having to block all the other threads? That's actually quite simple:

ConcurrentDictionary<int, YourObject> dictionary;return dictionary.GetOrAdd(id, i => CreateTestObject(i));

Note that CreateTextObject is called only if the key doesn't exist in the dictionary yet.