By default non-static methods have their own instance of variables for each thread when accessed via multiple threads, thus rendering them thread safe if they do not include a public variable etc.
On the other hand, variables in static methods are shared amongst threads rendering them non-thread safe by default.
Say, I have a class, having no static variables or methods whatsoever.
public class Profile {
private ConcurrentDictionary<int, int> cache =
new ConcurrentDictionary<int, int>();
public AddToCache() {
}
public RemoveToCache() {
}
public DoSomethingThatShouldBeThreadSafe() {
}
}
But then I create a static object from this class.
public static Profile objProfile = new Profile();
And then, objProfile is accessed with multiple threads.
The question is, are the methods of Profile class, AddToCache, RemoveFromCache and DoSomethingThatShouldBeThreadSafe, going to be thread safe or not when used through objProfile? Are their variables will be shared amongst threads, even if they are not static because the whole instance of the class is static?
As long as you only access the ConcurrentDictionary<> instance cache, and don't overwrite cache with a new instance in one of Profile-methods it is threadsafe.
Because of the second point, it's better to mark it readonly,
private readonly ConcurrentDictionary<int, int> cache =
new ConcurrentDictionary<int, int>();
because this says that you can write this member only during instantiation of Profile.
Although the ConcurrentDictionary<> itself is thread-safe, you still have the problem of non-atomicity of compound operations. Let's take a look at two possible GetFromCache() methods.
int? GetFromCacheNonAtomic(int key)
{
if (cache.ContainsKey(key)) // first access to cache
return cache[key]; // second access to cache
return null;
}
int? GetFromCacheAtomic(int key)
{
int value;
if (cache.TryGetValue(key, out value)) // single access to cache
return value;
return null;
}
only the second one is atomic, because it uses the ConcurrentDictionary<>.TryGetValue() method.
ConcurrentDictionary<> has the GetOrAdd() method, which takes a Func<TKey, TValue> delegate for non-existing values.
void AddToCacheIfItDoesntExist(int key)
{
cache.GetOrAdd(key, SlowMethod);
}
int SlowMethod(int key)
{
Thread.Sleep(1000);
return key * 10;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With