It can be done by adding wrapper which will able to replace value. To simplify code I will implement this wrapper with locks (to avoid double value construction).
First of all - interface. Please check if it reflects operations needed. I used int
type for keys and string
for value just to simplify example.
public delegate TValue GetNewValue<TValue>(TValue previousValue);
public interface IIntStringAtomicDictionary
{
/// <returns>true if was added, otherwise false</returns>
bool AddIfMissingOnly(int key, Func<string> valueGetter);
/// <returns>true if was updated, otherwise false</returns>
bool UpdateIfExists(int key, GetNewValue<string> convertPreviousValueToNew);
}
Implementation is below. It could not remove value, it can be done simple (I can update answer if you need)
public sealed class IntStringAtomicDictionary : IIntStringAtomicDictionary
{
private readonly ConcurrentDictionary<int, ValueWrapper<string>> _innerDictionary = new ConcurrentDictionary<int, ValueWrapper<string>>();
private readonly Func<int, ValueWrapper<string>> _wrapperConstructor = _ => new ValueWrapper<string>();
public bool AddIfMissingOnly(int key, Func<string> valueGetter)
{
var wrapper = _innerDictionary.GetOrAdd(key, _wrapperConstructor);
return wrapper.AddIfNotExist(valueGetter);
}
public bool UpdateIfExists(int key, GetNewValue<string> convertPreviousValueToNew)
{
var wrapper = _innerDictionary.GetOrAdd(key, _wrapperConstructor);
return wrapper.AddIfExists(convertPreviousValueToNew);
}
}
private sealed class ValueWrapper<TValue> where TValue : class
{
private readonly object _lock = new object();
private TValue _value;
public bool AddIfNotExist(Func<TValue> valueGetter)
{
lock (_lock)
{
if (_value is null)
{
_value = valueGetter();
return true;
}
return false;
}
}
public bool AddIfExists(GetNewValue<TValue> updateValueFunction)
{
lock (_lock)
{
if (!(_value is null))
{
_value = updateValueFunction(_value);
return true;
}
return false;
}
}
}
After writing the code we can re-read requirements. As I understand, we have to apply the following:
- Different keys should be updates from the different threads without locking.
- Value update should be atomic
- Parallel value adding if forbidden - please say if it is wrong
- Different values should be able to be created from the different threads.
Because of "parallel value adding" restriction we have to lock value creation. Therefore my wrapper above has this lock.
All other operations are not use any locks.
Additional improvements:
ValueWrapper
class can use ReadWriteLockSlim
to allow value read in parallel.
- Values can be removed with the same locks. Of course we can have race condition here.