Я пытаюсь использовать ConcurrentDictionary для реализации кеша с ограниченным ограничением. Когда кэш достигает своей емкости, дальнейшее добавление новых элементов отклоняется. Фрагмент кода выглядит следующим образом:
var result = this.cache.AddOrUpdate(
key,
(key1) =>
{
if (!this.IsFull())
{
return new List<MyObject> { value };
}
what to return here??
},
(key1, value1) =>
{
value1.Add(value);
return value1;
});
У меня вопрос, если кеш заполнен, что мне сюда возвращать? нулевой? или что?
Следует отметить, что в соответствии с документальный делегаты добавления и обновления вызываются вне блокировок, поэтому вы можете написать свой собственный код для этого, включая блокировку
Могу я заключить двух делегатов в lock (this) {…}?
Лучше всегда создавать объект только для блокировки и не использовать что-то вроде lock(this). Также AddOrUpdate будет иметь некоторую блокировку, но делегаты вызываются вне ее. Так что было бы лучше, если бы вы блокировали включенный весь код и просто не использовали AddOrUpdate. И действительно, в этот момент вы даже можете использовать обычный словарь, так как вы сами будете обрабатывать потокобезопасность.





У вас есть два варианта:
Ответ @Kirill Polishchuck является примером второй альтернативы, но я считаю, что он не безопасен для потоков, поэтому я бы изменил его так, чтобы он закончился так:
if (!this.IsFull())
{
// your logic here
this.cache.AddOrUpdate(key, k=> new List<MyObject> { value };);
}
else
{
// logic
}
В качестве первой альтернативы приведен пример того, что вы могли бы сделать для ее реализации. Мы реализуем интерфейс IDictonary<TKey,TValue> и блокируем те операции, которые этого требуют. Кроме того, класс не допускает дальнейших вставок, если был превышен _maxCount:
public class MaxCountDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> _dictionary;
private readonly object _lock;
public MaxCountDictionary(int maxCount) : this(maxCount, EqualityComparer<TKey>.Default) { }
public MaxCountDictionary(int maxCount, IEqualityComparer<TKey> equalityComparer)
{
_lock = new object();
MaxCount = maxCount;
_dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
public int MaxCount { get; }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public void Clear()
{
lock (_lock)
{
_dictionary.Clear();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
lock (_lock)
{
return ((IDictionary<TKey, TValue>) _dictionary).Contains(item);
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
lock (_lock)
{
((IDictionary<TKey, TValue>) _dictionary).CopyTo(array, arrayIndex);
}
}
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
public int Count
{
get
{
lock (_lock)
{
return _dictionary.Count;
}
}
}
public bool IsReadOnly => ((IDictionary<TKey, TValue>) _dictionary).IsReadOnly;
public bool ContainsKey(TKey key)
{
lock (_lock)
{
return _dictionary.ContainsKey(key);
}
}
public void Add(TKey key, TValue value)
{
lock (_lock)
{
if (_dictionary.Count < MaxCount) _dictionary.Add(key, value);
}
}
public bool Remove(TKey key)
{
lock (_lock)
{
return _dictionary.Remove(key);
}
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (_lock)
{
return _dictionary.TryGetValue(key, out value);
}
}
public TValue this[TKey key]
{
get
{
lock (_lock)
{
return _dictionary[key];
}
}
set
{
lock (_lock)
{
if (_dictionary.ContainsKey(key) || _dictionary.Count < MaxCount) _dictionary[key] = value;
}
}
}
public ICollection<TKey> Keys
{
get
{
lock (_lock)
{
return _dictionary.Keys.ToArray();
}
}
}
public ICollection<TValue> Values
{
get
{
lock (_lock)
{
return _dictionary.Values.ToArray();
}
}
}
public void AddOrUpdate(TKey key, TValue value, Func<TKey, TValue, TValue> updateValueFactory)
{
lock (_lock)
{
if (_dictionary.ContainsKey(key))
_dictionary[key] = updateValueFactory(key, value);
else if (_dictionary.Count < MaxCount) _dictionary[key] = value;
}
}
}
С помощью этой другой альтернативы вы просто передаете параметр MaxCount в свою коллекцию, вы можете изменить его, чтобы принять Func<bool>, чтобы словарь мог определить, добавлять ли дополнительные элементы или нет, таким образом вы можете передать ему свой метод IsFull.
Остерегаться: Это демонстрационный код, это просто образец, перечисление не является потокобезопасным, настройте его в соответствии с вашими потребностями.
Думаю, вы могли бы выбросить исключение. Или переместите кассету
IsFullпередAddOrUpdate.