업데이트이유는 NullReferenceException이
아래, 문제는 닷넷 4.6를 설치하는 순간을 고정 업데이트를 참조하십시오 던져 MemoryCache 않습니다.
나는 CacheItemPolicy
의 UpdateCallback
내에서 뭔가를 구현하고자합니다.
동일한 캐시 인스턴스 (MemoryCache.Default
)에서 여러 스레드를 실행하는 코드를 테스트하면 cache.Set
메서드를 호출 할 때 다음과 같은 예외가 발생합니다.
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCacheEntry.RemoveDependent(System.Runtime.Caching.MemoryCacheEntryChangeMonitor dependent = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCacheEntryChangeMonitor.Dispose(bool disposing = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.ChangeMonitor.DisposeHelper() C#
System.Runtime.Caching.dll!System.Runtime.Caching.ChangeMonitor.Dispose() C#
System.Runtime.Caching.dll!System.Runtime.Caching.ChangeMonitor.InitializationComplete() C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCacheEntryChangeMonitor.InitDisposableMembers(System.Runtime.Caching.MemoryCache cache = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCacheEntryChangeMonitor..ctor(System.Collections.ObjectModel.ReadOnlyCollection<string> keys = {unknown}, string regionName = {unknown}, System.Runtime.Caching.MemoryCache cache = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCache.CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys = {unknown}, string regionName = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCache.Set(string key = {unknown}, object value = {unknown}, System.Collections.ObjectModel.Collection<System.Runtime.Caching.ChangeMonitor> changeMonitors = {unknown}, System.DateTimeOffset absoluteExpiration = {unknown}, System.TimeSpan slidingExpiration = {unknown}, System.Runtime.Caching.CacheEntryUpdateCallback onUpdateCallback = {unknown}) C#
System.Runtime.Caching.dll!System.Runtime.Caching.MemoryCache.Set(string key = {unknown}, object value = {unknown}, System.Runtime.Caching.CacheItemPolicy policy = {unknown}, string regionName = {unknown}) C#
나는 MemoryCache
이 스레드 안전하므로 어떤 문제도 예상하지 않는다는 것을 알고 있습니다. 더 중요한 것은, 내가 UpdateCallback
을 지정하지 않으면 모든 것이 잘 작동한다는 것입니다!
좋아, 동작을 재현하기 위해 여기에 일부 콘솔 앱이 있습니다. 이 코드는 다른 라이브러리에서 수행중인 일부 테스트의 단순화 된 버전입니다. 다중 스레드 환경에서 충돌을 일으키는 것을 의미합니다. 한 스레드가 다른 스레드가 이미 스레드를 삭제하는 동안 하나의 스레드가 키/값을 읽으려고하면 ...
MemoryCache가 스레드 안전성을 가지기 때문에이 모든 것이 올바르게 작동합니다.
class Program
{
static void Main(string[] args)
{
var threads = new List<Thread>();
foreach (Action action in Enumerable.Repeat<Action>(() => TestRun(), 10))
{
threads.Add(new Thread(new ThreadStart(action)));
}
threads.ForEach(p => p.Start());
threads.ForEach(p => p.Join());
Console.WriteLine("done");
Console.Read();
}
public static void TestRun()
{
var cache = new Cache("Cache");
var numItems = 200;
while (true)
{
try
{
for (int i = 0; i < numItems; i++)
{
cache.Put("key" + i, new byte[1024]);
}
for (int i = 0; i < numItems; i++)
{
var item = cache.Get("key" + i);
}
for (int i = 0; i < numItems; i++)
{
cache.Remove("key" + i);
}
Console.WriteLine("One iteration finished");
Thread.Sleep(0);
}
catch
{
throw;
}
}
}
}
public class Cache
{
private MemoryCache CacheRef = MemoryCache.Default;
private string InstanceKey = Guid.NewGuid().ToString();
public string Name { get; private set; }
public Cache(string name)
{
Name = name;
}
public void Put(string key, object value)
{
var policy = new CacheItemPolicy()
{
Priority = CacheItemPriority.Default,
SlidingExpiration = TimeSpan.FromMinutes(1),
UpdateCallback = new CacheEntryUpdateCallback(UpdateCallback)
};
MemoryCache.Default.Set(key, value, policy);
}
public static void UpdateCallback(CacheEntryUpdateArguments args)
{
}
public object Get(string key)
{
return MemoryCache.Default[ key];
}
public void Remove(string key)
{
MemoryCache.Default.Remove(key);
}
}
실행하면 예외를 직접 받아야합니다. UpdateCallback 설정자를 주석 처리하면 더 이상 예외가 발생하지 않아야합니다. 또한 하나의 스레드 만 실행하면 (Enumerable.Repeat<Action>(() => TestRun(), 10)
에서 , 1)
으로 변경) 정상적으로 작동합니다.
나는 당신이 Update
또는 Remove
콜백을 설정할 때마다, MemoryCache
이 OnUpdateSentinel<your key>
같은 키를 당신을 위해 추가 감시 캐시 항목을 만들 것을 발견 : 나는 지금까지 무엇을 발견
. 슬라이딩 만료의 경우이 센티널 아이템 만 타임 아웃 세트를 가져 오기 때문에 해당 아이템에 대한 변경 모니터도 생성되는 것으로 보입니다! 이 항목이 만료되면 콜백이 호출됩니다.
내 추측은 ... 우리가 콜백을 정의 할 경우, 대략 같은 시간 같은 키/정책/콜백과 같은 항목을 만들려고하면 MemoryCache
내에서 문제가 있다는 것
또한 stacktrace에서 볼 수 있듯이 오류는 ChangeMonitor의 Dispose
메서드 내 어딘가에 나타납니다. CacheItemPolicy
에 변경 모니터를 추가하지 않았으므로 내부적으로 제어되는 것으로 보입니다 ...
이것이 맞으면 아마도 MemoryCache의 버그 일 수 있습니다. 나는 대개 그게 내 잘못이라고 생각하기 때문에 그 도서관에서 벌레를 찾는 것을 믿을 수 없다 : p, 어쩌면 나는 이것을 올바르게 구현하기에는 너무 어리 석다 ... 어떤 도움이나 힌트도 크게 평가 될 것이다;)
업데이트 2014 년 8 월 :
this issue을 수정하려고합니다.
업데이트 할 수 있습니다 2015 당신이, 예를 들어 설치하는 경우
는 문제가 해결 같은데 .Net 4.6과 함께 제공되는 VS 2015 RC. .Net이 프로젝트의 모든 버전에서 작동하기 때문에 .Net의 어떤 버전을 수정했는지 확인할 수 없습니다. .Net 4.5, 4.5.1 또는 4.5.2로 설정하면 오류가 더 이상 재생성되지 않습니다.
그래 솔루션 .Dispose 새로운 MemoryCache을 교체했다. 슬라이딩 만료에 대해서도 나는 마지막에 절대 만료를 사용하고 get에서 요소 (만료 시간 초과)를 수동으로 업데이트합니다. 이 ... 가 잘 볼 작품이 방법 https://cachemanager.codeplex.com/SourceControl/latest#src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle'1.cs 방법 당신이 그것을 재현 할 수있는 경우 'GetCacheItemInternal' – MichaC
하면, 당신은 할 수 있습니다 https://connect.microsoft.com/VisualStudio/feedback/details/817211/memorycache-throw-nullreferenceexception-if-cacheitempolicy-updatecallback-is-defined#tabs이에 – MichaC
업데이트 : 이것도 하나를 속도로? –