WeakReference.IsAlive
От: Аноним  
Дата: 22.09.06 11:24
Оценка:
Я вот тут написал классик для кэша, на слабых ссылках. Тоесть если на элемент кэша нигде не ссылаются, он может быть удалён сборщиком мусора, когда тот захочет. Внутри кэша 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 не сможет почистить этот объект, когда будет надо...
Re: WeakReference.IsAlive
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 22.09.06 11:28
Оценка: 1 (1) +1
Здравствуйте, Аноним, Вы писали:

А ты не спрашивай 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, т.е. если проверка показала что оно есть, следует вернуть объект)
— автоматическое удаление неиспользуемых объектов
Re[3]: WeakReference.IsAlive
От: dshe  
Дата: 22.09.06 12:09
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, 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 перестаёт гарантировать наличие объекта, а должна.

Блин...
Re[5]: WeakReference.IsAlive
От: Lloyd Россия  
Дата: 22.09.06 12:47
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.


А>Блин...


Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: WeakReference.IsAlive
От: Pavel M. Россия  
Дата: 22.09.06 12:56
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Здравствуйте, <Аноним>, Вы писали:


А>>Для кого лучше? Ведь тогда пользователю коллекции придётся делать две проверки — Contains перед и IsNull после GetItem. К томуже в таком случае проверка Contains перестаёт гарантировать наличие объекта, а должна.


А>>Блин...


L>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.


ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?
--------------------------
less think — do more
Re[7]: WeakReference.IsAlive
От: Lloyd Россия  
Дата: 22.09.06 12:59
Оценка:
Здравствуйте, Pavel M., Вы писали:

L>>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.


PM>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?


Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: WeakReference.IsAlive
От: Pavel M. Россия  
Дата: 22.09.06 13:03
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Здравствуйте, Pavel M., Вы писали:


L>>>Сделать gfhe операций LockKey/UnLockKey. Тогда между этими вызовами можно будет гарантировать, что Contains и GetItem будут вести себя согласованно.


PM>>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?


L>Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.

заблокировать можно, а если не разблокирует?
--------------------------
less think — do more
Re[5]: WeakReference.IsAlive
От: dshe  
Дата: 22.09.06 13:05
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, 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, первая для объекта из словаря и вторая для таргета. Моя задача и есть в том, чтобы не писать их повсеместно в коде, а спрятать в один класс. Вместе со стратегией очистки.
Re: WeakReference.IsAlive
От: Vie dodger  
Дата: 22.09.06 13:14
Оценка:
А как насчет метода bool TryGetItem(out ItemType item)?
Re[9]: WeakReference.IsAlive
От: Lloyd Россия  
Дата: 22.09.06 13:18
Оценка:
Здравствуйте, Pavel M., Вы писали:

PM>>>ты понимаешь, тут отстой в том, что будет, если кто-то вызовет Contains ,А GetItem забудет?


L>>Бросать exception, если кто-то обращается по ключу без пердварительного блокирования через LockKey.

PM>заблокировать можно, а если не разблокирует?

А пусть через using блокирует.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[7]: WeakReference.IsAlive
От: orangy Россия
Дата: 22.09.06 15:36
Оценка: 6 (1)
Здравствуйте, <Аноним>, Вы писали:

D>>Делай одну проверку IsNull и забей на Contains. Кстати, почему тебя не смущает тот факт, что между вызовами Contains и GetItem искомый объект будет не сколлекчен, а просто банально удален (мб в другом потоке)?


А>Одну проверку на IsNull делать нельзя. У меня есть Dictionary<TKey, WeakReference>. Если ключа в нём нету, он вернёт null. Тоесть при получении слабой ссылки и попытке дёрнуть её за .Target мы получим эксепшн. Так что две проверки IsNull, первая для объекта из словаря и вторая для таргета. Моя задача и есть в том, чтобы не писать их повсеместно в коде, а спрятать в один класс. Вместе со стратегией очистки.


Вы допускаете типичную ошибку, о которой много раз уже писали, ломали копья и т.п. См. например Asking questions where the answer is unreliable anyway (англ.)

Если говорить о словаре, то на какое время требуется гарантировать согласованность 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.
... << RSDN@Home 1.2.0 alpha rev. 655>>
"Develop with pleasure!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.