80

長時間実行されるプロセスなどの特定の状況では、ASP.NET キャッシュをロックして、そのリソースに対する別のユーザーによる後続の要求が、キャッシュにヒットする代わりに長いプロセスを再度実行することを回避することが重要であることを知っています。

ASP.NETでキャッシュロックを実装するC#での最良の方法は何ですか?

4

10 に答える 10

116

基本的なパターンは次のとおりです。

  • 値のキャッシュをチェックし、利用可能な場合は返します
  • 値がキャッシュにない場合は、ロックを実装します
  • ロック内で、キャッシュをもう一度確認してください。ブロックされている可能性があります
  • 値のルックアップを実行してキャッシュする
  • ロックを解除する

コードでは、次のようになります。

private static object ThisLock = new object();

public string GetFoo()
{

  // try to pull from cache here

  lock (ThisLock)
  {
    // cache was empty before we got the lock, check again inside the lock

    // cache is still empty, so retreive the value here

    // store the value in the cache here
  }

  // return the cached value here

}
于 2008-09-02T17:12:26.023 に答える
31

完全を期すために、完全な例は次のようになります。

private static object ThisLock = new object();
...
object dataObject = Cache["globalData"];
if( dataObject == null )
{
    lock( ThisLock )
    {
        dataObject = Cache["globalData"];

        if( dataObject == null )
        {
            //Get Data from db
             dataObject = GlobalObj.GetData();
             Cache["globalData"] = dataObject;
        }
    }
}
return dataObject;
于 2008-09-03T08:17:22.900 に答える
23

キャッシュ インスタンス全体をロックする必要はなく、挿入する特定のキーのみをロックする必要があります。つまり、男性用トイレを使用している間、女性用トイレへのアクセスをブロックする必要はありません:)

以下の実装では、並行辞書を使用して特定のキャッシュ キーをロックできます。このようにして、2 つの異なるキーに対して同時に GetOrAdd() を実行できますが、同じキーに対して同時に実行することはできません。

using System;
using System.Collections.Concurrent;
using System.Web.Caching;

public static class CacheExtensions
{
    private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>();

    /// <summary>
    /// Get or Add the item to the cache using the given key. Lazily executes the value factory only if/when needed
    /// </summary>
    public static T GetOrAdd<T>(this Cache cache, string key, int durationInSeconds, Func<T> factory)
        where T : class
    {
        // Try and get value from the cache
        var value = cache.Get(key);
        if (value == null)
        {
            // If not yet cached, lock the key value and add to cache
            lock (keyLocks.GetOrAdd(key, new object()))
            {
                // Try and get from cache again in case it has been added in the meantime
                value = cache.Get(key);
                if (value == null && (value = factory()) != null)
                {
                    // TODO: Some of these parameters could be added to method signature later if required
                    cache.Insert(
                        key: key,
                        value: value,
                        dependencies: null,
                        absoluteExpiration: DateTime.Now.AddSeconds(durationInSeconds),
                        slidingExpiration: Cache.NoSlidingExpiration,
                        priority: CacheItemPriority.Default,
                        onRemoveCallback: null);
                }

                // Remove temporary key lock
                keyLocks.TryRemove(key, out object locker);
            }
        }

        return value as T;
    }
}
于 2016-08-12T00:14:05.080 に答える
13

Pavel が言ったことを繰り返しますが、これが最もスレッドセーフな書き方だと思います

private T GetOrAddToCache<T>(string cacheKey, GenericObjectParamsDelegate<T> creator, params object[] creatorArgs) where T : class, new()
    {
        T returnValue = HttpContext.Current.Cache[cacheKey] as T;
        if (returnValue == null)
        {
            lock (this)
            {
                returnValue = HttpContext.Current.Cache[cacheKey] as T;
                if (returnValue == null)
                {
                    returnValue = creator(creatorArgs);
                    if (returnValue == null)
                    {
                        throw new Exception("Attempt to cache a null reference");
                    }
                    HttpContext.Current.Cache.Add(
                        cacheKey,
                        returnValue,
                        null,
                        System.Web.Caching.Cache.NoAbsoluteExpiration,
                        System.Web.Caching.Cache.NoSlidingExpiration,
                        CacheItemPriority.Normal,
                        null);
                }
            }
        }

        return returnValue;
    }
于 2010-06-28T19:23:38.807 に答える
2

Craig Shoemakerは、asp.netキャッシングで優れたショーを行いました:http: //polymorphicpodcast.com/shows/webperformance/

于 2008-09-02T10:00:37.327 に答える
1

最近、Correct State Bag Access Pattern と呼ばれる 1 つのパターンを見ましたが、これに触れているようです。

スレッドセーフになるように少し変更しました。

http://weblogs.asp.net/craigshoemaker/archive/2008/08/28/asp-net-caching-and-performance.aspx

private static object _listLock = new object();

public List List() {
    string cacheKey = "customers";
    List myList = Cache[cacheKey] as List;
    if(myList == null) {
        lock (_listLock) {
            myList = Cache[cacheKey] as List;
            if (myList == null) {
                myList = DAL.ListCustomers();
                Cache.Insert(cacheKey, mList, null, SiteConfig.CacheDuration, TimeSpan.Zero);
            }
        }
    }
    return myList;
}
于 2008-09-02T17:29:56.747 に答える
0

CodeGuruのこの記事では、さまざまなキャッシュロックのシナリオと、ASP.NETキャッシュロックのいくつかのベストプラクティスについて説明しています。

ASP.NETでのキャッシュアクセスの同期

于 2008-09-02T10:02:17.040 に答える
0

その特定の問題を解決するライブラリを作成しました: Rocks.Caching

また、この問題について詳しくブログに書き、なぜ重要なのかをここで説明しました。

于 2014-06-06T11:16:23.520 に答える