我在asp.net mvc应用程序中实现缓存,并在刷新缓存时防止查询多次运行我使用锁定任何数据库访问代码。
由于每个数据都有一个唯一的缓存键,我认为锁定该字符串可能是个好主意。但有时候要获取的数据对于特定用户是唯一的(例如,用户X写的所有注释),因此我使用String.Format()将用户的id放入缓存键中。
以下是一个例子:
private const string cacheKey = "blog_comments_from_specific_user-{0}";
private List<Comments> GetCommentsFromuser(int userId)
{
if (GetCache(String.Format(cacheKey, userId)) == null)
{
lock (String.Format(cacheKey, userId))
{
if (GetCache(String.Format(cacheKey, userId)) == null)
{
// get data from database
}
}
}
}
我希望数据库代码只针对每个唯一字符串内容运行一次,但我不确定它是否正在执行。我还没有找到测试它的方法。我读到有一个String.Intern()方法可以锁定特定的字符串内容,但我是否也需要它,或者String.Format()是否足够?
答案 0 :(得分:3)
不要锁定字符串。它们具有微妙的行为(实习,应用程序域重用,在其他位置无法预测的使用),使它们不适合这一点。如果你想要每个键锁定,创建一个字典(或类似的; Hashtable
具有更好的线程语义,但是非通用的)键(字符串)来锁定对象(可能只是new object()
)。 / p>
答案 1 :(得分:0)
String.Format
不一定返回相同的对象,即使两个字符串匹配:
var str1 = String.Format("{0}{1}", 1, 2);
var str2 = String.Format("{0}{1}", 1, 2);
Console.WriteLine(str1 == str2);
Console.WriteLine(Object.ReferenceEquals(str1, str2));
因此,锁定此类字符串的一个实例将无法与另一个实例上的锁正确同步。解决这个问题的一种方法是使用String interning的.NET机制。请参阅以下示例:
var str3 = String.Intern(String.Format("{0}{1}", 1, 2));
var str4 = String.Intern(String.Format("{0}{1}", 1, 2));
Console.WriteLine(str3 == str4);
Console.WriteLine(Object.ReferenceEquals(str3, str4));
另一方面,如前所述,是为每个String唯一分配一个锁定对象的单独集合。