C# 在对象内的函数调用期间,是否可以删除缓存中的对象?
鉴于代码:C# 在对象内的函数调用期间,是否可以删除缓存中的对象?,c#,caching,thread-safety,C#,Caching,Thread Safety,鉴于代码: private static readonly object GetMyObjLock = new object(); public static MyObject GetFromCache() { var key = "MyObj"; var cache = HttpContext.Current.Cache; if (cache[key] == null) { lock (GetMyObjLock) {
private static readonly object GetMyObjLock = new object();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}
电话:
var myObj = GetFromCache();
myObj.PerformMethod();
在另一个线程上侦听某些代码时:
if(message == "invalidate")
{
Httpcontext.Current.Cache.Remove("MyObj");
}
两个问题:
是否始终完成PerformMethod
方法和GetFromCache()
调用之间是否有机会使PerformMethod
等于myObj
null
var myObj = GetFromCache();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}
myObj.PerformMethod();
线程2:
if(message == "invalidate")
{
Httpcontext.Current.Cache.Remove("MyObj");
}
线程1:
var myObj = GetFromCache();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}
myObj.PerformMethod();
它会破坏你的实现
一种可能的修复方法是将新MyObject()
存储在局部变量中,然后将其添加到缓存中,然后返回局部变量。这将起作用,因为它是一个强有力的参考:
var myobject = new MyObject();
cache.Add(key,
myobject, null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
return myobject;
考虑线程的这种定时: 线程1:
var myObj = GetFromCache();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}
myObj.PerformMethod();
线程2:
if(message == "invalidate")
{
Httpcontext.Current.Cache.Remove("MyObj");
}
线程1:
var myObj = GetFromCache();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}
myObj.PerformMethod();
它会破坏你的实现
一种可能的修复方法是将新MyObject()
存储在局部变量中,然后将其添加到缓存中,然后返回局部变量。这将起作用,因为它是一个强有力的参考:
var myobject = new MyObject();
cache.Add(key,
myobject, null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
return myobject;
修复它的方法是使用
Add
函数的输出查看您添加的项是否实际添加到缓存中,如果它已经存在,它将返回上一个实例,如果它不存在,它将返回null
。当它返回null时,您只需将传入的对象设置为第二个参数,作为从函数返回的结果
private static readonly object GetMyObjLock = new object();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
MyObject result;
//Try to pull from the cache.
result = (MyObject)cache[key];
if(result == null)
{
//This lock is used to make sure the MyObject constructor is not called
// concurrently, it is not for thread safe access to the cache.
lock(GetMyObjLock)
{
//Check to see if anyone made a new object while we where waiting.
result = (MyObject)cache[key];
if(result == null)
{
var newObject = new MyObject();
//Try to add to cache, if the object was already added by someone who
// does not use the GetMyObjLock this function returns the previously
// added instance.
result = (MyObject)cache.Add(key,
newObject, null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
//If result is null then we successfully added a new
// item, assign it to result.
if(result == null)
result = newObject;
}
}
}
}
return result;
}
修复它的方法是使用
Add
函数的输出查看您添加的项是否实际添加到缓存中,如果它已经存在,它将返回上一个实例,如果它不存在,它将返回null
。当它返回null时,您只需将传入的对象设置为第二个参数,作为从函数返回的结果
private static readonly object GetMyObjLock = new object();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
MyObject result;
//Try to pull from the cache.
result = (MyObject)cache[key];
if(result == null)
{
//This lock is used to make sure the MyObject constructor is not called
// concurrently, it is not for thread safe access to the cache.
lock(GetMyObjLock)
{
//Check to see if anyone made a new object while we where waiting.
result = (MyObject)cache[key];
if(result == null)
{
var newObject = new MyObject();
//Try to add to cache, if the object was already added by someone who
// does not use the GetMyObjLock this function returns the previously
// added instance.
result = (MyObject)cache.Add(key,
newObject, null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
//If result is null then we successfully added a new
// item, assign it to result.
if(result == null)
result = newObject;
}
}
}
}
return result;
}
你所说的“PerformMethod将永远完成”是什么意思?你甚至没有给我们看这个方法的代码。我们怎么知道呢?@rory.ap我的意思是,如果在调用过程中侦听器将其从缓存中删除,那么它是否保证完成。它会抛出异常(例如“此对象不再存在”),还是会完成并删除它?从缓存中删除对象不会“销毁”它。它只是有资格进行垃圾收集。这不会发生,myObj参考文件确保了这一点。您似乎确实忘记锁定另一个线程,希望您没有。您所说的“将始终完成PerformMethod”是什么意思?你甚至没有给我们看这个方法的代码。我们怎么知道呢?@rory.ap我的意思是,如果在调用过程中侦听器将其从缓存中删除,那么它是否保证完成。它会抛出异常(例如“此对象不再存在”),还是会完成并删除它?从缓存中删除对象不会“销毁”它。它只是有资格进行垃圾收集。这不会发生,myObj参考文件确保了这一点。您似乎确实忘记了锁定另一个线程,希望您没有。了解缓存是线程安全的,但是假设
MyObject
构造函数很昂贵(比如它从数据库中提取数据),我们只想调用它一次。在这种情况下,锁定有意义吗?@TomGullen在这种情况下,是的,它有意义。还有,看看我更新的答案,我把它改了一点,你根本不需要循环。我将再次更新答案以使用锁。谢谢,这非常有帮助!理解缓存是线程安全的,但是假设MyObject
构造函数很昂贵(比如它从数据库中提取数据),我们只想调用它一次。在这种情况下,锁定有意义吗?@TomGullen在这种情况下,是的,它有意义。还有,看看我更新的答案,我把它改了一点,你根本不需要循环。我将再次更新答案以使用锁。谢谢,这非常有帮助!您应该检查cache.Add
的结果,如果它返回非空值,则传入的myobject
未添加到缓存中。Add
函数的作用更像是一个GetOrAdd
函数,除非它在成功添加时返回null
。@ScottChamberlain:如果每个用户都考虑锁(GetMyObjLock)
,那么这应该不是必需的,您应该检查缓存的结果。Add
,如果返回非空值,则传入的myobject
未添加到缓存中。Add
函数的作用更像是一个GetOrAdd
,除非它在成功添加时返回null
。@ScottChamberlain:如果每个用户都考虑了锁(GetMyObjLock)
,那么这应该不是必需的