常见缓存习语的线程含义
本文关键字:线程 缓存 习语 常见 | 更新日期: 2023-09-27 18:06:17
常见的缓存习惯用法是检查缓存中是否存在项,如果存在则检索,如果不存在则创建。
如果在注释的位置发生从Thread 1
到Thread 2
的上下文切换,那么当上下文切换回Thread 1
发生时,添加到缓存中的值可能会立即被覆盖。缺点是现在已经调用了两次calculateFooBar()
来计算相同的缓存项。这只是这个简单缓存实现的一个可接受的"次要"后果吗?是否通常不使用临界区,因为这会增加所有GetOrCreate
方法的开销?
Edit: _cache是对共享数据缓存的引用(例如ASP. js)。. NET数据缓存).
//not real C#
class FooBarDictionary
{
...
FooBar GetOrCreate(string key)
{
FooBar fooBar;
if (!_cache.TryGetValue(key, out fooBar))
{
fooBar = calculateFooBar(); //context switch occurs here
fooBars.Add(key, fooBar);
}
return fooBar;
}
}
如果你的程序工作正确,即使你有两个对象的实例,只有资源分配时间&内存是你的问题,我不会费心添加锁。从长远来看,线程同步的成本可能会更高。
对于这种"nolock"容器,ConcurrentDictionary<>将是一个很好的选择,就像其他人已经提到的那样。
一切都取决于这个_cache
变量是什么:它的类型和作用域。如果它是一个静态变量,并且不是线程安全的,例如Dictionary<TKey, TValue>
,则需要使用lock
来同步访问它。在。net 4.0中,ConcurrentDictionary<TKey, TValue>
类型以线程安全的方式实现了类似的功能。您的代码很好,但是如果没有lock
,就不能保证在很短的时间内不会对同一个键进行两次计算。
您可以查看下面的博客文章,了解该模式的一个很好的实现。如果你使用的是。net 4.0,你可以使用新的System.Runtime.Caching程序集
我们在几个场景中使用一些自定义缓存….NET 4中新的Concurrent
* -集合非常适合这样的实现…
如果foobars
是ConcurrentDictionary<string, FooBar>
,你可以这样做:
return foobars.GetOrAdd (key, (k) => calculateFooBar() );
此代码将替换示例方法GetOrCreate
的整个主体。
更多信息请参见http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/17/c.net-little-wonders-the-concurrentdictionary.aspx