я реализовал простой кэш-памяти, поддержанной ConcurrentDictionaryПочему значение, возвращаемое из ConcurrentDictionary, всегда равно null, когда используется несколько одновременных потоков?
public class MemoryCache
{
private ConcurrentDictionary<string, CacheObject> _memory;
public MemoryCache()
{
this._memory = new ConcurrentDictionary<string, CacheObject>();
}
private bool TryGetValue(string key, out CacheObject entry)
{
return this._memory.TryGetValue(key, out entry);
}
private bool CacheAdd(string key, object value, DateTime? expiresAt = null)
{
CacheObject entry;
if (this.TryGetValue(key, out entry)) return false;
entry = new CacheObject(value, expiresAt);
this.Set(key, entry);
return true;
}
public object Get(string key)
{
long lastModifiedTicks;
return Get(key, out lastModifiedTicks);
}
public object Get(string key, out long lastModifiedTicks)
{
lastModifiedTicks = 0;
CacheObject CacheObject;
if (this._memory.TryGetValue(key, out CacheObject))
{
if (CacheObject.HasExpired)
{
this._memory.TryRemove(key, out CacheObject);
return null;
}
lastModifiedTicks = CacheObject.LastModifiedTicks;
return CacheObject.Value;
}
return null;
}
public T Get<T>(string key)
{
var value = Get(key);
if (value != null) return (T)value;
return default(T);
}
public bool Add<T>(string key, T value)
{
return CacheAdd(key, value);
}
}
и теперь я пытаюсь проверить его с кодом, который основан офф blog post из @ayende.
var w = new ManualResetEvent(false);
var threads = new List<Thread>();
for (int i = 0; i < Environment.ProcessorCount; i++)
{
threads.Add(new Thread(() =>
{
w.WaitOne();
DoWork(i);
}));
threads.Last().Start();
}
w.Set();//release all threads to start at the same time
foreach (var thread in threads)
{
thread.Join();
}
Так DoWork вызывает процесс, который содержит одноплодную менеджер кэша и в моем случае это собирается покинуть и аутентификацию из системы и возвращает маркер. Этот токен затем сохраняется с помощью уникального ключа (имя пользователя). Теперь каждый из этих вызовов, в моем случае есть 8 ядер/потоков, имя пользователя такое же, скажем, «BobUser: CacheKey».
Каждый раз, когда я запускаю код, я вижу, что выполняется 8 запросов, так как кеш Get всегда возвращает null.
var token = _cm.Cache.Get<MyToken>(userId);
if (token != null) return token;
token = base.Logon(userId, password);
if (token != null)
{
_cm.Cache.Add(userId, token);
}
return token;
Действительно ли это из-за взаимодействия 8 потоков Точно в одно и то же время? Если это так, есть ли шаблон для исправления этой проблемы параллелизма?
Спасибо, Стивен
Я усугубил проблему с помощью многопоточного испытательного жгута. Этот код будет жить в хосте ASP.NET/WCF, где он должен «работать» достаточно хорошо. Мое решение во время тестирования состоит в том, чтобы перенести тест перед началом работы с одним запросом, который не имеет никаких утверждений, тем самым полностью продвигая его. –