2012-03-08 5 views
2

私はヘルパークラスをWebアプリケーションに持っていますが、一般的で不変なデータオブジェクトを静的プロパティとして提示することがあります。これらのオブジェクトを次のように読み込んでいます:キャッシュにデータをロードするためのロック

public static class MyWebHelper 
{ 
    #region - Constants & Fields 
    private const string LocationCacheKey = "MyWebHelper_Locations"; 
    private static object LocationLoadLock = new object(); 
    private static MemoryCache m_SiteCache; 
    #endregion 

    #region - Properties 
    /// <summary> 
    /// Gets the uneditable collection of locations. 
    /// </summary> 
    public static ReadOnlyCollection<Location> Locations 
    { 
     get 
     { 
      EnsureLocationsCache(); 
      return (ReadOnlyCollection<Location>)SiteCache[LocationCacheKey]; 
     } 
    } 

    /// <summary> 
    /// Gets the MemoryCache object specific for my site cache. 
    /// </summary> 
    public static MemoryCache SiteCache 
    { 
     get 
     { 
      if (m_SiteCache == null) 
      { 
       m_SiteCache = new MemoryCache("MyWeb_SiteCache"); 
      } 
      return m_SiteCache; 
     } 
    } 
    #endregion 

    #region - Methods 
    private static void EnsureLocationsCache() 
    { 
     lock (LocationLoadLock) 
     { 
      if (SiteCache[LocationCacheKey] == null) 
      { 
       // 
       // Load all Locations from the database and perform default sorting on location code. 
       // 
       List<Location> lLocations = DataAccess.GetAllLocations(); 
       lLocations.Sort(delegate(Location l1, Location l2) { return string.CompareOrdinal(l1.Code, l2.Code); }); 
       SiteCache[LocationCacheKey] = new ReadOnlyCollection<Location>(lLocations); 
      } 
     } 
    } 
    #endregion 
} 

私の質問は何ですか?私はデータベースへの呼び出しを減らそうとしていますが、オーバーヘッドを導入するだけのロックですか?キャッシュされたデータはサイト全体でかなり一般的に使用されており、ほとんど変更されません。また、私は正しい場所にロックしていますか?

+1

Lazy を見ると、ロック機構を抽象化するのに役立ちます。 – asawyer

+0

@asawyer私はLazy ソリューションのテストをコーディングし、正しく実行されているかどうかチェックしたいと考えています。私はこのコードを編集してこの質問に投稿するのか、この質問に答えるのか、あるいは新しい質問を投稿するべきですか? –

答えて

1

サーバへの各Webリクエストで新しいスレッドが作成され、静的ヘルパーがこれらのスレッド間で共有されるため、いくつかのタイプのロックが便利です。ロックがなければ、データベース読み込みがすでに進行中のときにEnsureLocationsCacheメソッドへの入力を危険にさらします。これで2回ロードされたデータの正確さには影響しませんが、DB読み取りが高価な場合は、全体的なパフォーマンスに影響を与え、キャッシングの効果を無効にします。

これは、実際には、アプリケーションの起動でEnsureLocationsCache()メソッドにアクセスしようとする同時スレッドの量に依存します。これは、各LocationCacheKeyに対して1回限りの呼び出しであるため低い可能性があります。

ロックを取得するオーバーヘッドは、キャッシュがすでにロードされている場合でもコードがロックを取得するため、有効な問題です。 @asawyer、@TomTom、@Joeはいくつかの選択肢を提案しています。

EDIT:

static constructorでEnsureLocationsCache()を呼び出すことを検討してください。この場合、ロックは一切必要ありません。

See discussion on thread-safety of static constructors

+0

-1が現実のものとなる。ロックは必要ありませんが、これはReadWriterLockでよりうまくいくことができ、dbアクセスが必要なときに完全ロックにエスカレートするだけです。ほとんどの場合、ロードされたデータはヒットし、リードロックのみがより効率的になります(パラレル読み取りが許可され、キャッシュへの書き込みが必要な場合のみシリアル化されます)。あなたはおそらくロックを少なくすることができます。ロックは素晴らしく、シンプルであり、技術的な意味では悲しげに素朴で重い解決策です。 – TomTom

+0

@TomTom ReadWriterLockソリューションは、nullをチェックする前にEnterReadLockを呼び出し、次にDBからキャッシュに読み込むためにEnterWriteLockを呼び出すでしょうか?コミュニティー全体に、私は間違った答えを受け入れましたか?もしそうなら、それを拒否するのがよい習慣でしょうか?そこにどのような影響があるか分かりません。 –

+0

負荷がかかっています。そうでなければ、読み取りの前に読み取りロックを行います。なぜなら、anotehrスレッドが書き込みロックを持っていれば、読み取りが停止するからです。したがって、何かが書かれていなければ、同時に100スレッドが読むことができます。キャッシュが通常正しい答えで満たされていれば...これは良いです。ただし、perforamcneには多少の違いがあります.1秒あたり数十万回のキャッシュループを実行するものを書きます。ナイーブなソリューションでは、そこまであなたを得ることはありません。 – TomTom

2

ロックを行う場合は、double-checked lockingを使用して、キャッシュから読み取るたびにロックを取得するオーバーヘッドが発生しないようにします。

私はまったくロックする必要も疑問に思います。ロックしないと、複数のスレッドが同時にキャッシュをリフレッシュしようとすることがありますが、これはまれであり、非常に高額でない限り、静的な読み取り専用データに問題は発生しません。

それ高価である場合、あなたは、@ asawyerさんのコメントでアドバイスに従うことができ、ロックがLazy<T>によって処理されるように、キャッシュにLazy<ReadOnlyCollection<Location>>を挿入します。

関連する問題