Reputation: 1601
I have a singleton class which looks a lot like this,
public class CfgHandler
{
private static readonly string ConfigDir = "Config";
public T Get<T>() where T : class, new()
{
string cfgFile = Path.Combine(ConfigDir, typeof(T).FullName + ".json");
if (File.Exists(cfgFile))
{
var reader = new JsonReader();
return reader.Read<T>(File.ReadAllText(cfgFile));
}
return null;
}
public void Set<T>(T instance) where T : class, new()
{
string cfgFile = Path.Combine(ConfigDir, typeof(T).FullName + ".json");
var writer = new JsonWriter();
string json = writer.Write(instance);
File.WriteAllText(cfgFile, json);
}
}
The class is used in a multithreaded environment and I want to add locks. But not one lock for the whole class, since I don't want a race condition between cfg.Set<Foo>();
and cfg.Set<Bar>()
as they work with different data.
I've thought about adding the following class to CfgHandler
,
private static class Locks<T>
{
private static object _lock = new object();
public static object Lock { get { return _lock; } }
}
and then lock like this (both for Get and Set),
public void Set<T>(T instance) where T : class, new()
{
lock(Locks<T>.Lock)
{
// save to disk
}
}
Am I missing something trivial? Is there a better way of achieving my goal?
Upvotes: 3
Views: 989
Reputation: 29342
Please see my question here for more details: Are static members of generic classes shared between types
The implementation you have is simple but effective, it will prevent concurrent access to the Set<T>(T Instance)
call correctly. My only advice is that the lock duration should be limited if you are making many concurrent calls to this API. For instance you could do all the work, but then only lock the call to the writer.write(instance)
call, which is the only non-threadsafe work you appear to be doing in the call.
As an aside you have the potential to improve your code on the Get call, please see my answer here Is there a way to check if a file is in use? regarding your check for the file existing.
Upvotes: 2
Reputation: 16553
Lock per instance or lock per type?
The way you are doing it (with a static Locks<T>.Lock
) means that every call to Set<Foo>
even on a different instance of CfgHandler will share the same lock. Is that what you want? I'm guessing you may be better off just locking per instance - it will save you the complexity of Locks<T>
. Just declare a private instance member (private object _lock = new object();
) and use it (lock(this._lock)
)
EDIT If you're using a singleton instance of CfgHandler
and want to lock per type, then I guess your approach is perfectly fine. If you're not using a single instance, but still want to lock per type then just make sure to use an instance of Locks<T>
instead of making it static.
Upvotes: 5