No, it is not sufficient to lock only on Add
.
The fact that it doesn't crash only tells you that it didn't crash during your test.
You cannot guarantee that:
- It won't crash in the future
- It will produce the correct results
A non-threadsafe data structure has no guarantees whatsoever if used in a multithreaded fashion.
You need to either:
- Lock on every call to it
- Use a threadsafe data structure, one that has been built to support this scenario
If you use a different data structure than a hashset, like a dictionary, you may even need to lock multi-statements, because this may still fail:
lock (dLock)
if (d.ContainsKey("test"))
return;
var value = ExpensiveCallToObtainValue();
lock (dLock)
d.Add("test", value);
Between the call to ContainsKey
and the call to Add
another thread may have already inserted that key.
To handle this correctly, without using a threadsafe data structure, is contain both operations inside the same lock:
lock (dLock)
{
if (!d.ContainsKey("test"))
d.Add("test", ExpensiveCallToObtainValue());
}