Добавление элемента в Microsoft.ApplicationServer.Caching.DataCache с пессимистической блокировкой?

Я работаю над уровнем кэширования на веб-сервере на стороне сервера, используя общее кэширование Azure, чтобы уменьшить количество запросов к базе данных и, таким образом, ускорить работу (надеюсь). Я зацикливаюсь на том, как сделать весь поток endevour безопасным. Кажется, я не нашел надежного и удобного способа заблокировать ключи в DataCache. Что мне не хватает, так это способ превентивной блокировки ключа до того, как на нем что-нибудь будет храниться, чтобы я мог добавить значение без риска того, что другой поток попытается сделать то же самое в то же время.

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

Я понял, что если я использую пессимистическую блокировку, я несу ответственность только за использование связанных с этим методов. Смешивание вещей приведет к нарушению всех механизмов блокировки (источник: http://go4answers.webhost4life.com/Example/datacacheput-unlocking-key-77158.aspx).

So basicly I only have access to these methods:
value GetAndLock(key, out DataCacheLockHandle);
void PutAndUnlock(key, value, DataCacheLockHandle);
void Unlock(key, DataCacheLockHandle);

Проблема в том, что GetAndLock выдает исключение, если я пытаюсь получить что-то, чего еще нет в кеше. В то же время мой единственный метод добавления чего-либо в кеш - это «PutAndUnlock», и его нельзя использовать, если я не выполнил успешное «GetAndUnlock».

Фактически, невозможно добавить что-либо новое в кеш, единственное, что можно сделать, это заменить то, что уже есть (что будет ничем).

Мне кажется, что я вынужден использовать оптимистичный «Put» в случае, когда «GetAndLock» генерирует исключение «ничего там». Однако, согласно тому, что я читал, оптимистичный «Put» разрушает любую существующую блокировку, достигнутую с помощью «GetAndLock», так что это уничтожит всю попытку обеспечения безопасности потоков.

Example plan:
1. Try to GetAndLock
2. In case of nothing there exception: 
     - Put a dummy item on the key. 
     - GetAndLock again.
3. We have a lock, do computations, query database etc
4. PutAndUnlock the computed value


One of probably several ways it would screw up:
Thread1: Tries to GetAndLock, gets nothing there exception
Thread2: Tries to GetAndLock, gets nothing there exception
Thread1: Put a dummy item on the key
Thread1: GetAndLock again, lock achieved
Thread2: Put a dummy item on the key (destroying Thread1:s lock)
Thread2: GetAndLock again, lock achieved
Thread1: We think we have a lock, do computations, query database etc
Thread2: We have a lock, do computations, query database etc
Thread1: PutAndUnlock the computed value (will this throw an exception?)
Thread2: PutAndUnlock the computed value

По сути, два потока могут одновременно записывать разные вещи в один и тот же ключ, игнорируя блокировки, которые, по их мнению, у них есть.

Мой единственный вывод может заключаться в том, что пессимистическая блокировка DataCache является неполной функцией и полностью непригодна для использования. Я что-то упускаю? Есть ли способ решить эту проблему?

Все, что мне не хватает, - это способ превентивно заблокировать ключ до того, как на нем что-нибудь сохранится.


person Jonatan Melin    schedule 12.03.2013    source источник
comment
GetAndLock имеет параметр forceLock, где msdn говорит: Если forceLock истинно, ключ блокируется независимо от наличия пары ключ-значение в кеше. Разве это не поможет вам заблокировать ключ, даже если состояние для этого ключа не существует?   -  person psulek    schedule 23.04.2015


Ответы (1)


Джонатан,

Вы учли эту логику для добавления вещей в кеш (простите, пожалуйста, мой псевдокод)?

public bool AddToCache (строковый ключ, значение объекта) {

DataCache dc = _factory.GetDefaultCache();  
object currentVal = dc.Get(key);

if (currentVal == null) {
    dc.Put(key, value);
    currentVal = dc.GetAndLock(key);

    if (value == currentVal) {
        //handle this rare occurrence + unlock.
        return false;
    } else {
                   dc.Unlock(key);
            }
} else {
    currentVal = dc.GetAndLock(key);
    dc.PutAndUnlock (key, value);
}

return true;

}

person SemanticZen    schedule 15.03.2013
comment
Я не уверен, что полностью слежу за ним. Вы имеете в виду добавить код для обработки случая, когда блокировка была прервана? - person Jonatan Melin; 15.03.2013
comment
Хм, читая еще раз, полагаю, вы не это имели в виду. Проблема с созданием Get, чтобы проверить, нет ли ключа, заключается в том, что другой поток может делать то же самое, пока вы работаете, поэтому вы можете дополнить его двумя параллельными потоками, выполняющими Put. Этот Put потенциально может уничтожить полученный GetAndLock другого потока. - person Jonatan Melin; 15.03.2013
comment
Идея состоит в том, чтобы: 1. проверить, существует ли он 2. если он не существует, добавить его 3. проверить его снова (с блокировкой) 4. если новое значение НЕ совпадает с только что добавленным значением, тогда какой-то другой поток изменил его между проверкой существования значения и перед установкой на него блокировки - ›так что вам придется добавить некоторую логику для обработки этой ситуации. - person SemanticZen; 15.03.2013
comment
Я понимаю. Я считаю, что это тоже может потерпеть неудачу, но по-другому. Поток 1: Проверяет с помощью Get Thread 2: Проверяет с помощью Get Thread 1: Проверяет с помощью Get Thread 3: Проверяет с помощью Get Thread 1: Блокирует, чтобы проверить, изменилось ли значение, но не изменилось Поток 1: Разблокирует поток 3: currentVal равно не нуль, делает нормальный случай Lock Thread 2: Делает ли Put, разрушая Lock потока 3 - person Jonatan Melin; 19.03.2013
comment
Тем не менее, у меня сейчас есть план, который я в основном закодировал, но он еще не свободен от ошибок. Я опубликую об этом, когда думаю, что это так. Общая идея заключается в том, что я добавляю фиктивный элемент в кеш при запуске приложения, чтобы у меня было что-то, что я знаю, что я могу заблокировать, а затем я использую это как своего рода металлический замок для всего кеша, чтобы можно было проверять и добавлять которые идут непрерывно. - person Jonatan Melin; 19.03.2013
comment
сделать метод статическим и заблокировать - person Sean; 06.02.2014