Иногда возникает необходимость кэшировать результат выполнения какой-либо функции между несколькими потоками. Конструкция
. Ниже представлен generic-класс для реализации подобного хэширования.
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
/// <summary>
/// Потокобезопасный кэш
/// </summary>
/// <typeparam name="TKey">Тип ключа</typeparam>
/// <typeparam name="TValue">Тип значения</typeparam>
public sealed class Cache<TKey, TValue>
{
/// <summary>
/// Делегат для получения значения
/// </summary>
/// <param name="keyVal">ключ</param>
/// <returns>значение</returns>
public delegate TValue HowToObtainValue(TKey keyVal);
/// <summary>
/// Получение значения из кэша
/// </summary>
/// <param name="key">ключ</param>
/// <returns></returns>
public TValue Get(TKey key)
{
return Get(key, obtainer);
}
/// <summary>
/// Получение значения из кэша
/// </summary>
/// <param name="key">ключ</param>
/// <param name="howToObtainValue">делегат, используемый в случае
/// отсутсвия значения к кэше</param>
/// <returns></returns>
public TValue Get(TKey key, HowToObtainValue howToObtainValue)
{
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
TValue theValue;
if(!dic.TryGetValue(key, out theValue))
{
LockCookie lc = rwl.UpgradeToWriterLock(Timeout.Infinite);
try
{
theValue = howToObtainValue(key);
dic.Add(key, theValue);
}
finally
{
rwl.DowngradeFromWriterLock(ref lc);
}
}
return theValue;
}
finally
{
rwl.ReleaseReaderLock();
}
}
/// <summary>
/// Констркутор
/// </summary>
/// <param name="underlyingDictionaryImplementation">реализация IDictionary</param>
/// <param name="func">получение параметра</param>
public Cache( IDictionary<TKey,TValue> underlyingDictionaryImplementation,
HowToObtainValue func)
{
dic = underlyingDictionaryImplementation;
obtainer = func;
rwl = new ReaderWriterLock();
}
public Cache(HowToObtainValue func)
: this(new Dictionary<TKey, TValue>(), func)
{ }
/// <summary>
/// Конструктор по умолчанию
/// </summary>
public Cache():this(null)
{}
private ReaderWriterLock rwl;
private IDictionary<TKey, TValue> dic;
private HowToObtainValue obtainer;
#if DEBUG
/// <summary>
/// !!! Пример использования !!!
/// </summary>
public static void Sample()
{
Cache<int, int> cache1 = new Cache<int, int>(
delegate(int keyVal)
{
Trace.WriteLine("Вычисляем для " + keyVal);
return keyVal*keyVal;
}
);
for (int i = 1; i < 100; ++i)
{
Trace.WriteLine(cache1.Get(i));
}
for (int i = 1; i < 100; ++i)
{
Trace.WriteLine(cache1.Get(i));
}
}
#endif
}