Да, для эффективной реализации кэша необходим механизм быстрого поиска, поэтому List<T>
является неправильной структурой данных.Dictionary<TKey, TValue>
- это идеальная структура данных для кэша, поскольку она позволяет заменить:
var value = instance.GetValueExpensive(key);
на:
var value = instance.GetValueCached(key);
, используя кэшированные значения в словаре и используясловарь, чтобы сделать тяжелую работу для поиска.Вызывающий не мудр.
Но, если вызывающие абоненты могут звонить из нескольких потоков, тогда .NET4 предоставляет ConcurrentDictionary<TKey, TValue>
, который отлично работает в этой ситуации.Но что кэширует словарь?Похоже, в вашей ситуации ключом словаря является child , а значениями словаря являются результаты базы данных для этого потомка.
OK, так что теперь у нас есть поток-безопасный и эффективный кеш результатов базы данных по ключу ребенка.Какую структуру данных мы должны использовать для результатов базы данных?
Вы еще не сказали, как выглядят эти результаты, но, поскольку вы используете LINQ, мы знаем, что они по крайней мере IEnumerable<T>
и, возможно, даже List<T>
.Итак, мы вернулись к той же проблеме, верно?Поскольку List<T>
не является потокобезопасным, мы не можем использовать его для значения словаря.Или мы можем?
Кэш должен быть доступен только для чтения с точки зрения вызывающей стороны.Вы говорите с LINQ, что вы «делаете вещи», такие как add / remove, но это не имеет смысла для кэшированного значения .Имеет смысл выполнять такие вещи в реализации самого кэша, как замена устаревшей записи новыми результатами.
Значение словаря, поскольку оно доступно только для чтения, может бытьList<T>
с без побочных эффектов , даже если к нему будут обращаться из нескольких потоков.Вы можете использовать List<T>.AsReadOnly
, чтобы повысить свою уверенность и добавить некоторую проверку безопасности во время компиляции.
Но важный момент заключается в том, что List<T>
не является поточно-ориентированным, только если оно изменчиво.Поскольку по определению метод, реализованный с использованием кэша, должен возвращать одно и то же значение, если он вызывается несколько раз (до тех пор, пока значение не будет аннулировано самим кэшем), клиент не может изменить возвращенное значение, и поэтому List<T>
должен быть заморожен, фактически неизменяемым.
Если клиенту крайне необходимо изменить результаты кэшированной базы данных, а значение - List<T>
, то единственный безопасный способ сделать это:
- Сделать копию
- Внести изменения в копию
- Попросить кэш обновить значение
В итоге используйте поточно-безопасный словарь для кэша верхнего уровня и обычныйсписок для кэшированного значения, будьте осторожны, никогда не изменяйте содержимое последнего после вставки его в кеш.