条件付き追加の要件については、常にを使用します。これには、オブジェクトをビルドする必要がある場合に起動するデリゲートを受け入れるConcurrentDictionary
オーバーロードされたメソッドがあります。GetOrAdd
ConcurrentDictionary<string, object> _cache = new
ConcurrenctDictionary<string, object>();
public void GetOrAdd(string key)
{
return _cache.GetOrAdd(key, (k) => {
//here 'k' is actually the same as 'key'
return buildDataUsingGoodAmountOfResources();
});
}
実際には、ほとんどの場合、static
同時辞書を使用します。以前は「通常の」辞書をReaderWriterLockSlim
インスタンスで保護していましたが、.Net 4に切り替えるとすぐに(それ以降のみ使用可能になります)、遭遇した辞書のいずれかを変換し始めました。
ConcurrentDictionary
のパフォーマンスは控えめに言っても立派です:)
年齢のみに基づく有効期限セマンティクスでNaive実装を更新します。また、@ usrの提案に従って、個々のアイテムが1回だけ作成されるようにする必要があります。 @usrが示唆しているように、再度更新しますLazy<T>
。aを使用する方がはるかに簡単です。並行ディクショナリに追加するときに、作成デリゲートをそのデリゲートに転送できます。実際には私のロックの辞書はとにかく機能しなかったので、私はコードを変更しました。しかし、私は本当にそれを自分で考えるべきでした(しかし、ここ英国では真夜中過ぎで、私は打ち負かされています。同情はありますか?もちろんそうではありません。開発者である私は、死者を目覚めさせるのに十分なカフェインを静脈に流しています)。
IRegisteredObject
ただし、これを使用してインターフェイスを実装し、それをHostingEnvironment.RegisterObject
メソッドに登録することをお勧めします。これを行うと、アプリケーションプールがシャットダウン/リサイクルされたときにポーラースレッドをシャットダウンするためのよりクリーンな方法が提供されます。
public class ConcurrentCache : IDisposable
{
private readonly ConcurrentDictionary<string, Tuple<DateTime?, Lazy<object>>> _cache =
new ConcurrentDictionary<string, Tuple<DateTime?, Lazy<object>>>();
private readonly Thread ExpireThread = new Thread(ExpireMonitor);
public ConcurrentCache(){
ExpireThread.Start();
}
public void Dispose()
{
//yeah, nasty, but this is a 'naive' implementation :)
ExpireThread.Abort();
}
public void ExpireMonitor()
{
while(true)
{
Thread.Sleep(1000);
DateTime expireTime = DateTime.Now;
var toExpire = _cache.Where(kvp => kvp.First != null &&
kvp.Item1.Value < expireTime).Select(kvp => kvp.Key).ToArray();
Tuple<string, Lazy<object>> removed;
object removedLock;
foreach(var key in toExpire)
{
_cache.TryRemove(key, out removed);
}
}
}
public object CacheOrAdd(string key, Func<string, object> factory,
TimeSpan? expiry)
{
return _cache.GetOrAdd(key, (k) => {
//get or create a new object instance to use
//as the lock for the user code
//here 'k' is actually the same as 'key'
return Tuple.Create(
expiry.HasValue ? DateTime.Now + expiry.Value : (DateTime?)null,
new Lazy<object>(() => factory(k)));
}).Item2.Value;
}
}