Это можно сделать, добавив оболочку, которая сможет заменить значение.Для упрощения кода я реализую эту обертку с блокировками (чтобы избежать создания двойного значения).
Прежде всего - интерфейс.Пожалуйста, проверьте, отражает ли это необходимые операции.Я использовал int
тип для ключей и string
для значения только для упрощения примера.
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);
}
Реализация приведена ниже.Невозможно удалить значение, это можно сделать просто (я могу обновить ответ, если вам нужно)
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;
}
}
}
После написания кода мы можем перечитать требования.Как я понимаю, мы должны применить следующее:
- Разные ключи должны быть обновлениями из разных потоков без блокировки.
- Значение обновления должно быть атомарным
- Параллельнодобавление значения, если оно запрещено, скажите, пожалуйста, если оно неверно.
- Из разных потоков можно создавать разные значения.
Из-за ограничения "параллельного добавления значений" мы должныблокировка создания значения.Поэтому моя обертка выше имеет эту блокировку.
Все остальные операции не используют никаких блокировок.
Дополнительные улучшения:
ValueWrapper
класс может использовать ReadWriteLockSlim
чтобы разрешить параллельное считывание значений. - Значения могут быть удалены с помощью одинаковых блокировок.Конечно, у нас могут быть условия гонки здесь.