Блок приложения кэширования библиотеки Microsoft Enterprise не является поточно-ориентированным? - PullRequest
4 голосов
/ 20 августа 2009

Я создал супер простое консольное приложение для тестирования блока приложения кэширования Enterprise Library, и его поведение вызывает недоумение. Я надеюсь, что я ввернул что-то, что легко исправить в настройке. Срок действия каждого предмета истекает через 5 секунд для целей тестирования.

Базовая настройка - «Каждую секунду выбирайте число от 0 до 2. Если в кеше его еще нет, поместите его в него - в противном случае просто извлеките его из кеша. Сделайте это внутри оператора LOCK, чтобы убедиться, что поток безопасности.

APP.CONFIG:

<configuration>
  <configSections>
    <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <cachingConfiguration defaultCacheManager="Cache Manager">
    <cacheManagers>
      <add expirationPollFrequencyInSeconds="1" maximumElementsInCacheBeforeScavenging="1000"
      numberToRemoveWhenScavenging="10" backingStoreName="Null Storage"
      type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
      name="Cache Manager" />
    </cacheManagers>
    <backingStores>
      <add encryptionProviderName="" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
      name="Null Storage" />
    </backingStores>
  </cachingConfiguration>
</configuration>

C #:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

namespace ConsoleApplication1
{
    class Program
    {
        public static ICacheManager cache = CacheFactory.GetCacheManager("Cache Manager");
    static void Main(string[] args)
        {
            while (true)
            {
                System.Threading.Thread.Sleep(1000); // sleep for one second.
                var key = new Random().Next(3).ToString();
                string value;
                lock (cache)
                {
                    if (!cache.Contains(key))
                    {
                        cache.Add(key, key, CacheItemPriority.Normal, null, new SlidingTime(TimeSpan.FromSeconds(5)));
                    }
                    value = (string)cache.GetData(key);
                }
                Console.WriteLine("{0} --> '{1}'", key, value);
                //if (null == value) throw new Exception(); 
            }
        }
    }
}

OUTPUT - Как я могу предотвратить возврат кеша нулей?

2 --> '2'
1 --> '1'
2 --> '2'
0 --> '0'
2 --> '2'
0 --> '0'
1 --> ''
0 --> '0'
1 --> '1'
2 --> ''
0 --> '0'
2 --> '2'
0 --> '0'
1 --> ''
2 --> '2'
1 --> '1'
Press any key to continue . . .

Ответы [ 6 ]

10 голосов
/ 20 августа 2009

Вы видите, что срок действия вашего CacheItem истек из-за 5-секундного истечения срока действия SlidingTime.

Перед возвратом кэшированного значения метод GetData выполняет проверку, чтобы определить, не истек ли срок действия CacheItem. Если срок его действия истек, CacheItem удаляется из кэша и возвращается значение null. Тем не менее, вызов Contains вернет true, потому что CacheItem находится в кеше, даже если срок его действия истек. Это похоже на дизайн. Имея это в виду, было бы разумно не кэшировать нулевое значение для представления данных, поскольку вы не сможете отличить устаревший CacheItem от фактического кэшированного значения null.

Предполагая, что вы не кэшируете нулевое значение, тогда решение Люка должно подойти вам:

value = cache.GetData(key) as string;

// If null was returned then it means that there was no item in the cache 
// or that there was an item in the cache but it had expired 
// (and was removed from the cache)
if (value == null)
{
    value = key;
    cache.Add(key, value, CacheItemPriority.Normal, null,
        new SlidingTime(TimeSpan.FromSeconds(5)));
}


Для получения дополнительной информации см. Подробное руководство по корпоративной библиотеке Microsoft .

7 голосов
/ 20 августа 2009

Я заметил, что вы, похоже, получаете null обратно из кэша всякий раз, когда к этому элементу не обращались в течение предыдущих 5 итераций цикла (т. Е. 5 секунд). Может ли это быть связано с вашим 5-секундным временем истечения?

Это кажется маловероятным, но, возможно, у вас есть состояние гонки, и предметы выпадают из кэша между проверкой Contains и извлечением GetData.

Попробуйте это изменение и посмотрите, имеет ли это какое-либо значение для вывода:

while (true)
{
    System.Threading.Thread.Sleep(1000);

    var key = new Random().Next(3).ToString();
    string value;

    lock (cache)
    {
        value = (string)cache.GetData(key);
        if (value == null)
        {
            value = key;
            cache.Add(key, value, CacheItemPriority.Normal, null,
                new SlidingTime(TimeSpan.FromSeconds(5)));
        }
    }
    Console.WriteLine("{0} --> '{1}'", key, value);
}
3 голосов
/ 03 июня 2010

Одна из причин, по которой .Contains может возвращаться как true, а .GetData может возвращать null, заключается в том, что .GetData проходит через всю систему устаревания (кажется, что возвращаются только данные, expired) и .Contains не проверяет, не истек ли срок его содержимого.

{
    cache.Add("key", "value", CacheItemPriority.Normal, 
              null, new SlidingTime(TimeSpan.FromSeconds(5)));
    System.Threading.Thread.Sleep(6000);
    Console.WriteLine(cache.Contains("key"));        /// true
    Console.WriteLine(cache.GetData("key") != null); /// false
    Console.WriteLine(cache.Contains("key"));        /// false
}

Другая проблема, с которой я столкнулся, заключалась в том, что я не мог сказать, содержал ли кэш запись с нулевым значением в качестве значения, или кеш просто не содержал запись для ключа. Обходной путь, который я использую, заключается в том, что если .GetData возвращается с null и .Contains равно true, то null целенаправленно хранился в кэше и срок его действия не истек.

1 голос
/ 03 мая 2011

Немного стар, однако сегодня я столкнулся с очень похожей проблемой: если я извлекаю значение из кэша, срок действия которого истекает (плюс до 25 дополнительных секунд), я получаю нулевое значение. Однако Microsoft утвердила эту ситуацию и предложила исправить здесь , мне просто нужно выяснить, как ее реализовать.

1 голос
/ 20 августа 2009

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

if (!cache.Contains(key))
{
    lock(mylockobj)
    {
        if (!cache.Contains(key))
        {
             cache.Add(key, key)
        }
    }
}

Также возможно загляните в CacheItemRemovedCallback.

0 голосов
/ 29 сентября 2012

Я знаю, что этот вопрос довольно старый, но проблема в том, что вы используете Contains, а затем пытаетесь получить значение. В промежутке между вызовами Contains и GetData срок действия элемента истек и он был удален, поэтому GetData вернул ноль. Это известно как состояние гонки.

Решение довольно простое (без использования замков), не используйте Contains

private static void CachingBlockTest()
{
    while (true)
    {
        System.Threading.Thread.Sleep(2000);

        var key = new Random().Next(3).ToString();
        string value = cache.GetData(key) as string;

        if (value == null)
        {
            value = key;
            cache.Add(key, value, CacheItemPriority.Normal, new RefreshAction(),
                new SlidingTime(TimeSpan.FromSeconds(5)));
        }
        Console.WriteLine("{0} --> '{1}'", key, value);
    } 
}
private class RefreshAction : ICacheItemRefreshAction
{
    public void Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
    {
        Console.WriteLine("{0} --> {1} removed", removedKey, expiredValue);
    }
}
...