Я вот тут написал классик для кэша, на слабых ссылках. Тоесть если на элемент кэша нигде не ссылаются, он может быть удалён сборщиком мусора, когда тот захочет. Внутри кэша Dictionary<TKey, WeakReference>, и у кэша есть методы Contains и GetItem. Метод Contains проверяет, содержится ли объект в кэше по ключу и не удалён ли он, т.е. свойство IsAlive у экземпляра WeakReference. Метод GetItem возвращает свойство Target WeakReference-а.
Так вот, меня теперь мучают сомнения. Вот получу я что объект жив, и соберусь вызвать GetItem. А злобный GC возьмёт и сколлектит мой айтем, и тогда я получу null. А буду думать что там объект. В качестве решения я завёл в классе кэша поле private TItem m_guard и перед проверкой IsAlive запоминаю туда Target. А после GetItem делаю m_guard = null;. Вот.
Собственно, вопрос. Я конечно понимаю, что это велосипед. Может быть кто знает как это делается по-человечески? Ведь если я проверю наличие, а получать не буду, то GC не сможет почистить этот объект, когда будет надо...
А ты не спрашивай IsAlive. Ты сразу бери Target. Если объект уже помер, то Target будет null
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[2]: WeakReference.IsAlive
От:
Аноним
Дата:
22.09.06 11:44
Оценка:
Здравствуйте, xvost, Вы писали:
X>А ты не спрашивай IsAlive. Ты сразу бери Target. Если объект уже помер, то Target будет null
Так нельзя. От класса кэш требуется следующая функциональность:
— проверка наличия объекта в кэше;
— получение объекта по ключу (не должно быть null, т.е. если проверка показала что оно есть, следует вернуть объект)
— автоматическое удаление неиспользуемых объектов
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, xvost, Вы писали:
X>>А ты не спрашивай IsAlive. Ты сразу бери Target. Если объект уже помер, то Target будет null
А>Так нельзя. От класса кэш требуется следующая функциональность:
А>- проверка наличия объекта в кэше; А>- получение объекта по ключу (не должно быть null, т.е. если проверка показала что оно есть, следует вернуть объект) А>- автоматическое удаление неиспользуемых объектов
Тогда требования, которые предъявляются к кешу, противоречивы поскольку допускают описанную тобой ситуацию (Contains вернул true, а GetItem -- null).
Можно, правда, еще ввести понятие транзакции и установить, что ни один объект не может быть сколлекчен в ее рамках. И, соответсвенно, вызывать Contains и GetItem в рамках одной транзакции. Для того, чтобы это реализовать тебе скорее всего придется в начале транзакции дампить все элементы кеша в отдельную strong reference коллекцию, а в конце транзакции ее очищать.
...Полагаю, что все-таки, решение при котором GetItem возвращает null, лучше.
--
Дмитро
Re[4]: WeakReference.IsAlive
От:
Аноним
Дата:
22.09.06 12:28
Оценка:
Здравствуйте, dshe, Вы писали:
D>Можно, правда, еще ввести понятие транзакции и установить, что ни один объект не может быть сколлекчен в ее рамках. И, соответсвенно, вызывать Contains и GetItem в рамках одной транзакции. Для того, чтобы это реализовать тебе скорее всего придется в начале транзакции дампить все элементы кеша в отдельную strong reference коллекцию, а в конце транзакции ее очищать.
А зачем все, если достаточно только тех, которые нужны?
D>...Полагаю, что все-таки, решение при котором GetItem возвращает null, лучше.
Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.
Здравствуйте, <Аноним>, Вы писали:
А>Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.
А>Блин...
Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, <Аноним>, Вы писали:
А>>Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.
А>>Блин...
L>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.
ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?
Здравствуйте, Pavel M., Вы писали:
L>>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.
PM>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?
Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Pavel M., Вы писали:
L>>>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.
PM>>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?
L>Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.
заблокировать можно, а если не разблокирует?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, dshe, Вы писали:
D>>Можно, правда, еще ввести понятие транзакции и установить, что ни один объект не может быть сколлекчен в ее рамках. И, соответсвенно, вызывать Contains и GetItem в рамках одной транзакции. Для того, чтобы это реализовать тебе скорее всего придется в начале транзакции дампить все элементы кеша в отдельную strong reference коллекцию, а в конце транзакции ее очищать.
А>А зачем все, если достаточно только тех, которые нужны?
так он ведь еще этого не знает какие будут нужны
D>>...Полагаю, что все-таки, решение при котором GetItem возвращает null, лучше. А>Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.
Делай одну проверку IsNull и забей на Contains. Кстати, почему тебя не смущает тот факт, что между вызовами Contains и GetItem искомый объект будет не сколлекчен, а просто банально удален (мб в другом потоке)?
--
Дмитро
Re[8]: WeakReference.IsAlive
От:
Аноним
Дата:
22.09.06 13:07
Оценка:
Здравствуйте, Lloyd, Вы писали:
L>Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.
Ну тогда всё получается, как я уже сделал. Лочить запрашиваемую ссылку в Contains перед проверкой и отпускать в GetItem после получения. Осталось только бросать эксепшен если вызывается GetItem без Contains. Но проблемы это не решает.. Хотя она и не такая серьёзная — больше одного объекта в памяти никогда не застрянет, и тот только до следующего вызова Contains, но однако ж кривовато получается...
Re[6]: WeakReference.IsAlive
От:
Аноним
Дата:
22.09.06 13:12
Оценка:
Здравствуйте, dshe, Вы писали:
D>Делай одну проверку IsNull и забей на Contains. Кстати, почему тебя не смущает тот факт, что между вызовами Contains и GetItem искомый объект будет не сколлекчен, а просто банально удален (мб в другом потоке)?
Одну проверку на IsNull делать нельзя. У меня есть Dictionary<TKey, WeakReference>. Если ключа в нём нету, он вернёт null. Тоесть при получении слабой ссылки и попытке дёрнуть её за .Target мы получим эксепшн. Так что две проверки IsNull, первая для объекта из словаря и вторая для таргета. Моя задача и есть в том, чтобы не писать их повсеместно в коде, а спрятать в один класс. Вместе со стратегией очистки.
Здравствуйте, Pavel M., Вы писали:
PM>>>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?
L>>Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey. PM>заблокировать можно, а если не разблокирует?
Здравствуйте, <Аноним>, Вы писали:
D>>Делай одну проверку IsNull и забей на Contains. Кстати, почему тебя не смущает тот факт, что между вызовами Contains и GetItem искомый объект будет не сколлекчен, а просто банально удален (мб в другом потоке)?
А>Одну проверку на IsNull делать нельзя. У меня есть Dictionary<TKey, WeakReference>. Если ключа в нём нету, он вернёт null. Тоесть при получении слабой ссылки и попытке дёрнуть её за .Target мы получим эксепшн. Так что две проверки IsNull, первая для объекта из словаря и вторая для таргета. Моя задача и есть в том, чтобы не писать их повсеместно в коде, а спрятать в один класс. Вместе со стратегией очистки.
Если говорить о словаре, то на какое время требуется гарантировать согласованность Contains и Get? 1 миллисекунда? 10 миллисекунд? 24 строчки кода? 150 инструкций IL? Единственной гранулярностью, которую вы условно можете гарантировать — это искуственно созданная, как вам и предложили сделать путем "транзакций". Иначе говоря, вам действительно нужно уметь блокировать таблицу или объекты, при этом если производительность не является последней в вашем списке приоритетов, то гранулярность блокировки тоже важна. Проще всего в данном случае сделать "захват" объекта, т.е. получить по ключу сразу Target и не парить голову. Кэш он такой — либо даст либо не даст Если не даст — всё равно надо строить заново и складывать в кэш. Так зачем куча проверок?
object obj = cache.Get(id); // здесь уже объект никуда не денется, если он естьif (obj == null) // объекта в кэше не оказалось
{
obj = Load(id); // грузим его откуда-то еще
cache.Put(id, obj); // здесь кэш уже проверит, что за время наших размышлений никто не положил под тем же id что-нибудь в кэш
}
... // использовать объект, нам тут уже всё равно, из кэша он взялся или нет
Также, если вы собираетесь действительно сделать кэш, а не словарь слабых ссылок — почитайте про стратегии работы с кешами хотя бы на примере System.Web.Cache.