Is this ValueStore class threadsafe? Does the lock scope in GetInt(string key) need to be extended around the yield return?
public class ValueStore
{
private readonly object _locker = new object();
private readonly Dictionary<string, int> _data =
new Dictionary<string, int>();
public ValueStore(Dictionary<string, int> data)
{
_data = data;
}
public IEnumerable<int> GetInt(string key)
{
IEnumerable<KeyValuePair<string, int>> selected;
lock(_locker)
{
selected = _data.Where(x => x.Key.Equals(key));
}
foreach (KeyValuePair<string, int> pair in selected)
{
yield return pair.Value;
}
}
}
The unit test seems to be fine:
[TestFixture]
public class ValueStoreTest
{
[Test]
public void test1()
{
Dictionary<string, int> data = new Dictionary<string, int>();
for (int i = 0; i < 100000; i++)
{
data.Add(i.ToString(),i);
}
ValueStore vs = new ValueStore(data);
for (int i = 0; i < 900000; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
for (int j = 0; j < 100000; j++)
{
IEnumerable<int> d = vs.GetInt(j.ToString());
}
});
}
}
}
No, it’s definitely not thread-safe.
The fact that it uses a dictionary passed in by the client means you have no control over when the client changes it. You’re also only locking it when you apply the
Whereclause – but that doesn’t actually perform any iteration at all. You’d need to hold the lock while iterating over the results – but as I said before, it doesn’t stop the client from changing the dictionary at any time anyway.If you created the dictionary in the class and only ever exposed the data within it (i.e. protected it from the outside world) you could make it fully thread-safe. If you insist that the client code doesn’t mutate the dictionary, you don’t need the lock at all, as dictionaries are safe to read from multiple threads when there are no writers.