更新:如果此方法不是线程安全的,这是可以接受的,但我有兴趣了解如何使其成为线程安全的。key
此外,如果我可以避免它,我不想锁定所有值的单个对象。
原始问题:假设我想编写一个高阶函数,它接受一个键和一个函数,并检查一个对象是否已经用给定的键缓存。如果有,则返回缓存的值。否则,运行给定的函数并缓存并返回结果。
这是我的代码的简化版本:
public static T CheckCache<T>(string key, Func<T> fn, DateTime expires)
{
object cache = HttpContext.Current.Cache.Get(key);
//clearly not thread safe, two threads could both evaluate the below condition as true
//what can I lock on since the value of "key" may not be known at compile time?
if (cache == null)
{
T result = fn();
HttpContext.Current.Cache.Insert(key, result, null, expires, Cache.NoSlidingExpiration);
return result;
}
else
return (T)cache;
}
另外,假设我不知道key
编译时的所有可能值。
我怎样才能使这个线程安全?我知道我需要在这里引入锁定,以防止 1+ 个线程将我的条件评估为 true,但我不知道要锁定什么。我读过的许多关于锁定的示例(例如Jon Skeet 的文章)都建议使用仅用于锁定的“虚拟”私有变量。在这种情况下这是不可能的,因为在编译时密钥是未知的。我知道我可以通过为 each 使用相同的锁来轻松地使这个线程安全key
,但这可能是浪费。
现在,我的主要问题是:
是否可以锁定key
?字符串实习在这里有帮助吗?
在阅读了 .NET 2.0 string interning inside out 之后,我了解到我可以显式调用String.Intern()
以获取从字符串值到字符串实例的 1 对 1 映射。 这个适合锁吗?我们把上面的代码改成:
public static T CheckCache<T>(string key, Func<T> fn, DateTime expires)
{
//check for the scenario where two strings with the same value are stored at different memory locations
key = String.Intern(key);
lock (key) //is this object suitable for locking?
{
object cache = HttpContext.Current.Cache.Get(key);
if (cache == null)
{
T result = fn();
HttpContext.Current.Cache.Insert(key, result, null, expires, Cache.NoSlidingExpiration);
return result;
}
else
return (T)cache;
}
}
上面的实现线程安全吗?