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.