Условное добавление C# ConcurrentDictionary

Я пытаюсь использовать 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;
    });

У меня вопрос, если кеш заполнен, что мне сюда возвращать? нулевой? или что?

Думаю, вы могли бы выбросить исключение. Или переместите кассету IsFull перед AddOrUpdate.

juharr 15.10.2018 02:04

Следует отметить, что в соответствии с документальный делегаты добавления и обновления вызываются вне блокировок, поэтому вы можете написать свой собственный код для этого, включая блокировку

juharr 15.10.2018 02:09

Могу я заключить двух делегатов в lock (this) {…}?

Yinfang Zhuang 15.10.2018 19:11

Лучше всегда создавать объект только для блокировки и не использовать что-то вроде lock(this). Также AddOrUpdate будет иметь некоторую блокировку, но делегаты вызываются вне ее. Так что было бы лучше, если бы вы блокировали включенный весь код и просто не использовали AddOrUpdate. И действительно, в этот момент вы даже можете использовать обычный словарь, так как вы сами будете обрабатывать потокобезопасность.

juharr 15.10.2018 20:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
487
1

Ответы 1

У вас есть два варианта:

  • Напишите новую структуру параллельных данных для управления ограничениями кеша. Это сложнее, но если такая структура будет использоваться в нескольких местах в вашем решении, лучше использовать эту.
  • Делегируйте управление ограничениями кеша классу, который фактически хранит кеш. Это проще.

Ответ @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.

Остерегаться: Это демонстрационный код, это просто образец, перечисление не является потокобезопасным, настройте его в соответствии с вашими потребностями.

Другие вопросы по теме