Is it necessary to use a lock(lockObj){} block when I fill my ConcurrentDictionary here? For a little background this will be used in a MVC application though I suspect the scenario question is relevant for any multi-threaded application.
In searching stackoverflow I didn’t find this exact scenario. Where the first time a value is requested from the GetOptionById value is called, it could be called by two separate threads.
1) Would it be considered better practice to make the List objects value a private static that you lock around in hopes of not calling the database multiple times before the ConcurrentDictionary is filled?
2) Is that (#1 above) even necessary or is the ConcurrentDictionary smart enough to work this out on it’s own? Thanks in advance for any input.
public class MyOptions
{
static string GetOptionById(int id)
{
if (options == null || options.Count <= 0)
FillOptionList();
return options[id];
}
static void FillOptionList()
{
List<MyBusinessObject> objects = DataAccessLayer.GetList();
foreach (MyBusinessObject obj in objects)
options.TryAdd(obj.Id, obj.Name);
}
private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
}
EDIT: Thanks everyone for your input, would this be a safer approach?
public static string OptionById(int id)
{
if (!options.ContainsKey(id))
{
//perhaps this is a new option and we need to reload the list
FillOptionsOrReturn(true /*force the fill*/);
return (!options.ContainsKey(id)) ? "Option not found" : options[id];
}
else
return options[id];
}
private static void FillOptionsOrReturn(bool forceFill = false)
{
List<MyBusinessClass> objectsFromDb = null;
lock (lockObj)
{
if (forceFill || options == null || options.Keys.Count <= 0)
reasons = DataAccessLayer.GetList();
}
if (objectsFromDb != null)
{
foreach (MyBusinessClass myObj in objectsFromDb)
options.TryAdd(myObj.id, myObj.name);
}
}
private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
private static object lockObj = new object();
No, the methods on this data structure are already thread-safe.
Maybe, especially if
GetListitself were not already thread-safe. Except that what you propose would not work. ThatList<MyBusinessObject>instance is returned fromGetListso you cannot lock something that does not exist yet. Instead, you would create a separate object for locking purposes only.No, there is no magic that goes on that would somehow cause
GetListto be executed serially.By the way, your
GetOptionByIdhas a race condition. More than one thread could get inside theifblock at the same time. Your code might attempt to initialize the dictionary more than once.