Microsoft企业库缓存应用程序块不是线程安全的?

Ala*_*anR 4 .net c# caching enterprise-library thread-safety

我创建了一个超级简单的控制台应用程序来测试企业库缓存应用程序块,行为令人困惑.我希望我搞砸了一些容易在设置中修复的东西.为了测试目的,我让每个项目在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>
Run Code Online (Sandbox Code Playgroud)

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(); 
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出 - 如何防止缓存返回空值?

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 . . .
Run Code Online (Sandbox Code Playgroud)

Ran*_*ica 10

您看到的是由于5秒SlidingTime到期,您的CacheItem已过期.

在返回缓存值之前,GetData方法执行检查以查看CacheItem是否已过期.如果它已过期,则从缓存中删除CacheItem并返回null.但是,对Contains的调用将返回true,因为CacheItem位于缓存中,即使它已过期也可能已过期.这似乎是设计上的.考虑到这一点,最好不要缓存空值来表示没有数据,因为您无法从实际缓存的null值中识别过期的CacheItem.

假设您没有缓存空值,那么Luke的解决方案应该适合您:

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)));
}
Run Code Online (Sandbox Code Playgroud)


有关详细信息,请参阅Microsoft企业库的权威指南.


Luk*_*keH 7

我注意到,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);
}
Run Code Online (Sandbox Code Playgroud)