常见缓存习语的线程含义

本文关键字:线程 缓存 习语 常见 | 更新日期: 2023-09-27 18:06:17

常见的缓存习惯用法是检查缓存中是否存在项,如果存在则检索,如果不存在则创建。

如果在注释的位置发生从Thread 1Thread 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 * -集合非常适合这样的实现…

如果foobarsConcurrentDictionary<string, FooBar>,你可以这样做:

return foobars.GetOrAdd (key, (k) => calculateFooBar() );

此代码将替换示例方法GetOrCreate的整个主体。

更多信息请参见http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/17/c.net-little-wonders-the-concurrentdictionary.aspx