我需要一个需要计算和缓存的属性,这是很常见的。
通常我会使用锁和布尔顶部检查是否已处理。有时我会在访问者中这样做。
这种方法的性能影响是什么?有没有更好的方法呢。
我对此的常用方法的示例代码:
Sub Main()
Dim X AS New X()
For i AS Integer = 0 To 50
Dim Thr AS New Threading.Thread(ADdressOF X.ProcessData )
Thr.Start()
Next
End Sub
Private Class X
Private DataCached AS Boolean
Private ProcessedData AS String
Private Lock AS New Object()
Public Function ProcessData() AS String
Synclock Lock
IF NOT DataCached Then
DataCached = True
ProcessedData = DoStuff()
End If
End Synclock
Console.Writeline(ProcessedData)
Return ProcessedData
End Function
Function DoStuff() AS String
Threading.Thread.Sleep(1000)
Console.Writeline("Processed")
return "stuff"
End Function
End Class
编辑:
这是需要在访问时计算的东西,因为它不断变化。构造函数计算在这里没有帮助。 (示例是我正在做的的真正简化版本)
答案 0 :(得分:2)
您可以通过双重检查优化来提高并发性:
If Not DataCached Then
Synclock Lock
If Not DataCached Then
ProcessedData = DoStuff()
DataCached = True ' Set this AFTER processing
End If
End Synclock
这将避免在第一个init之后的关键部分。
答案 1 :(得分:2)
从不计算两次是否至关重要?即如果两个线程发生同时要求它,并独立计算该值,那是一个显示阻止?在大多数情况下,它不是 - 在这种情况下,只检查null
(因为它是一个字符串):( C#中的例子,道歉):
if(processedData == null) {
processedData = DoStuff();
}
return processedData;
所有后续调用应该会看到新值(如果隐藏在属性/方法中,我认为我们不需要volatile
)。
这具有无锁且简单的优点。
另一个技巧是使用嵌套类的静态属性:
string SomeValue {
get {return MyCache.SomeValue;}
}
static class MyCache {
public static readonly string SomeValue;
static MyCache() {
SomeValue = DoStuff();
}
}
这是懒惰计算的,但静态初始化程序的规则意味着它只保证运行一次(不包括反射)。
答案 2 :(得分:0)
这是唯一的方法,可能还有其他系统库可以做到这一点,但最终该库也会在内部做同样的事情。
答案 3 :(得分:0)
Firslty,我会在您的类之外移动包含业务逻辑的缓存,并保持您的业务逻辑纯,并允许您独立于应用程序控制缓存。但那不是你的问题......
你需要没有提及你是否会多次冒险,可能会多次计算这些东西,直到缓存很热。简单的方法是:
if (Cache["key"] == null)
Cache["key"] = obj.processData();
return Cache["key"];
缓存本身应该确保这是安全的。
如果你希望在填充缓存时显式阻塞,那么你已经在上面的代码中有了这样做的语义,但是我推荐这个改变:
if (Cache["key"] == null) {
Synclock blockIfProcessing
if (Cache["key"] == null)
Cache["key"] = obj.processData();
End Synclock
}
return Cache["key"];
基本上,一旦缓存很热,这会阻止您在每次通话时阻止,并且会产生更好的性能,同时保护您免受潜在竞争条件的影响。
请记住,只要你有两个不同的锁,你就可以打开潜在的死锁(这远远超出了这个线程的范围)。
寻找.Net缓存解决方案。