Comments 12
А почему вы использовали Dictionary c навешенной на него блокировкой вместо стандартного ConcurrentDictionary?
PS и откуда такое ограничение TValue: class? Структуры кэшировать вроде как не надо?
PS и откуда такое ограничение TValue: class? Структуры кэшировать вроде как не надо?
0
Нет смысла использовать ConcurrentDictionary, т.к. мне всё равно нужно блокировать всю коллекцию для очищения мёртвых ссылок.
WeakReference принимает только class и на практике мне не нужно кэшировать структуры. Что int кэшировать что ли? Свои структуры я использую крайне редко, только когда это продиктовано ограничениями по производительности (нужно часто и по многу создавать экземпляры).
WeakReference принимает только class и на практике мне не нужно кэшировать структуры. Что int кэшировать что ли? Свои структуры я использую крайне редко, только когда это продиктовано ограничениями по производительности (нужно часто и по многу создавать экземпляры).
0
WeakReference нельзя использовать для кэша. По сути, уходить сущности из вашего кэша будут не по мере заполнения памяти, а в случайные моменты времени.
Посмотрите в сторону System.Runtime.Caching
msdn.microsoft.com/ru-ru/library/ms404247.aspx
Avoid using weak references as an automatic solution to memory management problems. Instead, develop an effective caching policy for handling your application's objects.
Посмотрите в сторону System.Runtime.Caching
msdn.microsoft.com/ru-ru/library/ms404247.aspx
Avoid using weak references as an automatic solution to memory management problems. Instead, develop an effective caching policy for handling your application's objects.
+2
Давайте я подробнее опишу для чего я сделал именно такую реализацию, может появятся идеи как лучше сделать.
У меня в базе хранится список лучших статей за день (грубо говоря 100 значений типа long). Также у меня есть 100500 юзеров (ориентир именно на 100к, хотя реально поменьше сейчас). Итоговый список лучших статей для каждого юзера уникальный, то есть его надо генерить на основании общего списка и некоторых данных по юзеру. Для распределения работы я использую шину (RabbitMQ).
Загружаю из базы все ид юзеров и пачками по 100 отправляю их на шину (то есть в одном сообщении лежит 100 ид юзеров), а также ключ для списка лучших статей.
В обработчике, я поднимаю из базы список лучших статей, поднимаю данные по каждому юзеру, нахожу какие конкретно статьи нужно отправить данному юзеру и отправляю ему письмо.
Описанный выше кэш, я сделал, чтобы в рамках одного воркера не нужно было хотя бы каждый раз загружать список лучших статей. Они поднимутся один раз, при обработке первого пакета и будут висеть.
Более хорошего, чем WeakReference способа очистки я сходу не придумал, т.к. воркеры без состояния, они не могут знать были все данные отправлены или нет и не знают когда надо очистить кэш.
Второй вариант у меня был просто по времени, но показалось как-то не красиво.
У меня в базе хранится список лучших статей за день (грубо говоря 100 значений типа long). Также у меня есть 100500 юзеров (ориентир именно на 100к, хотя реально поменьше сейчас). Итоговый список лучших статей для каждого юзера уникальный, то есть его надо генерить на основании общего списка и некоторых данных по юзеру. Для распределения работы я использую шину (RabbitMQ).
Загружаю из базы все ид юзеров и пачками по 100 отправляю их на шину (то есть в одном сообщении лежит 100 ид юзеров), а также ключ для списка лучших статей.
В обработчике, я поднимаю из базы список лучших статей, поднимаю данные по каждому юзеру, нахожу какие конкретно статьи нужно отправить данному юзеру и отправляю ему письмо.
Описанный выше кэш, я сделал, чтобы в рамках одного воркера не нужно было хотя бы каждый раз загружать список лучших статей. Они поднимутся один раз, при обработке первого пакета и будут висеть.
Более хорошего, чем WeakReference способа очистки я сходу не придумал, т.к. воркеры без состояния, они не могут знать были все данные отправлены или нет и не знают когда надо очистить кэш.
Второй вариант у меня был просто по времени, но показалось как-то не красиво.
0
… и будут висеть.
Проблема в том, что не понятно сколько они там будут висеть.
Посмотрите Алгоритмы кэширования. Скорее всего под Вашу задачу что-нибудь подойдёт. LRU или еще чего.
0
Вот вы начали грузить статьи, они попали в нулевое поколение, прямую ссылку на них убрали. Нагрузили таких статей 256 кб. Прошла сборка мусора, ваш кэш пуст…
0
Не совсем так: воркер получил порцию юзеров, для которых надо поработать. Он взял из кэша данные и держит в локальной переменной. Значит данные не умрут по он их не отпустит. Параллельный поток воркера тоже смог взять данные из кэша и так далее, пока все юзеры не обработаны. А дальше, именно как мне надо: все юзеры по данным статьям обработаны, и пусть данные умирают при первой же сборке.
0
Не надо писать 2 раза
Пока вы в UpgradeableReadLock никто ничего не запишет
if (data.TryGetValue(key, out wr))
{
val = (TValue)wr.Target;
if (val != null)
return val;
}
Пока вы в UpgradeableReadLock никто ничего не запишет
0
Да, но любое количество читателей могут встать в очередь на EnterWriteLock и после моей первой записи войти туда и опять начать дёргать базу. А это нам не нужно.
0
Кстати, это — серьезная проблема производительности. Я бы предложил такое решение:
Скрытый текст
public TValue this[TKey key]
{
get
{
CleanCache();
WeakReference wr;
TValue val;
try
{
rwLock.EnterReadLock();
if (data.TryGetValue(key, out wr))
{
val = (TValue)wr.Target;
if (val != null)
return val;
}
}
finally
{
rwLock.ExitReadLock();
}
try
{
rwLock.EnterWriteLock();
if (data.TryGetValue(key, out wr))
{
val = (TValue)wr.Target;
if (val != null)
return val;
}
data[key] = new WeakReference(val = getter(key));
return val;
}
finally
{
rwLock.ExitWriteLock();
}
}
}
0
Sign up to leave a comment.
Простой кэш в памяти